Skip to content

Commit 1d4a215

Browse files
authored
reconcile allowedaddresses in nic (#83)
* reconcile allowedaddresses in nic Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * wait fo server to become ready Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * delete machines without providerID Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * add tests to allowedAddresses Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * fix linter Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * resolve comments Signed-off-by: Felix Breuer <f.breuer94@gmail.com> * resolve comments Signed-off-by: Felix Breuer <f.breuer94@gmail.com> --------- Signed-off-by: Felix Breuer <f.breuer94@gmail.com>
1 parent f69b95a commit 1d4a215

12 files changed

+461
-58
lines changed

go.sum

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,4 @@ sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxO
234234
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
235235
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
236236
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
237-
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
237+
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

pkg/provider/apis/provider_spec.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ type ProviderSpec struct {
2828
// Optional field. If not specified, the server may use default networking or require manual configuration.
2929
Networking *NetworkingSpec `json:"networking,omitempty"`
3030

31+
// AllowedAddresses are the IP address ranges (CIDRs) allowed to originate traffic from the server's network interface.
32+
// Optional field. If specified, these ranges are configured as AllowedAddresses on the network interface of the server to bypass anti-spoofing rules.
33+
AllowedAddresses []string `json:"allowedAddresses,omitempty"`
34+
3135
// SecurityGroups are the names of security groups to attach to the server
3236
// Optional field. If not specified, the project's default security group will be used.
3337
SecurityGroups []string `json:"securityGroups,omitempty"`

pkg/provider/apis/validation/validation.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package validation
88
import (
99
"encoding/json"
1010
"fmt"
11+
"net"
1112
"regexp"
1213

1314
api "github.com/stackitcloud/machine-controller-manager-provider-stackit/pkg/provider/apis"
@@ -165,6 +166,15 @@ func ValidateProviderSpecNSecret(spec *api.ProviderSpec, secrets *corev1.Secret)
165166
}
166167
}
167168

169+
// Validate AllowedAddresses
170+
if len(spec.AllowedAddresses) > 0 {
171+
for _, cidr := range spec.AllowedAddresses {
172+
if _, _, err := net.ParseCIDR(cidr); err != nil {
173+
errors = append(errors, fmt.Errorf("providerSpec.allowedAddresses has an invalid CIDR: %s", cidr))
174+
}
175+
}
176+
}
177+
168178
// Validate AffinityGroup
169179
if spec.AffinityGroup != "" {
170180
if !isValidUUID(spec.AffinityGroup) {

pkg/provider/core.go

Lines changed: 124 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import (
1010
"encoding/base64"
1111
"errors"
1212
"fmt"
13+
"slices"
14+
"strings"
1315

1416
"github.com/gardener/machine-controller-manager/pkg/util/provider/driver"
1517
"github.com/gardener/machine-controller-manager/pkg/util/provider/machinecodes/codes"
1618
"github.com/gardener/machine-controller-manager/pkg/util/provider/machinecodes/status"
19+
api "github.com/stackitcloud/machine-controller-manager-provider-stackit/pkg/provider/apis"
1720
"github.com/stackitcloud/machine-controller-manager-provider-stackit/pkg/provider/apis/validation"
1821
"k8s.io/klog/v2"
1922
)
@@ -182,27 +185,106 @@ func (p *Provider) CreateMachine(ctx context.Context, req *driver.CreateMachineR
182185
createReq.Metadata = providerSpec.Metadata
183186
}
184187

185-
// Call STACKIT API to create server
186-
server, err := p.client.CreateServer(ctx, projectID, providerSpec.Region, createReq)
188+
// check if server already exists
189+
server, err := p.getServerByName(ctx, projectID, providerSpec.Region, req.Machine.Name)
187190
if err != nil {
188-
klog.Errorf("Failed to create server for machine %q: %v", req.Machine.Name, err)
189-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create server: %v", err))
191+
klog.Errorf("Failed to fetch server for machine %q: %v", req.Machine.Name, err)
192+
return nil, status.Error(codes.Unavailable, fmt.Sprintf("failed to fetch server: %v", err))
190193
}
191194

192-
// Generate ProviderID in format: stackit://<projectId>/<serverId>
193-
providerID := fmt.Sprintf("%s://%s/%s", StackitProviderName, projectID, server.ID)
195+
if server == nil {
196+
// Call STACKIT API to create server
197+
server, err = p.client.CreateServer(ctx, projectID, providerSpec.Region, createReq)
198+
if err != nil {
199+
klog.Errorf("Failed to create server for machine %q: %v", req.Machine.Name, err)
200+
return nil, status.Error(codes.Unavailable, fmt.Sprintf("failed to create server: %v", err))
201+
}
202+
}
194203

195-
// NodeName is the machine name (will register with this name in Kubernetes)
196-
nodeName := req.Machine.Name
204+
if err := p.patchNetworkInterface(ctx, projectID, server.ID, providerSpec); err != nil {
205+
klog.Errorf("Failed to patch network interface for server %q: %v", req.Machine.Name, err)
206+
return nil, status.Error(codes.Unavailable, fmt.Sprintf("failed to patch network interface for server: %v", err))
207+
}
197208

209+
// Generate ProviderID in format: stackit://<projectId>/<serverId>
210+
providerID := fmt.Sprintf("%s://%s/%s", StackitProviderName, projectID, server.ID)
198211
klog.V(2).Infof("Successfully created server %q with ID %q for machine %q", server.Name, server.ID, req.Machine.Name)
199212

200213
return &driver.CreateMachineResponse{
201214
ProviderID: providerID,
202-
NodeName: nodeName,
215+
NodeName: req.Machine.Name,
203216
}, nil
204217
}
205218

219+
func (p *Provider) getServerByName(ctx context.Context, projectID, region, serverName string) (*Server, error) {
220+
// Check if the server got already created
221+
labelSelector := map[string]string{
222+
StackitMachineLabel: serverName,
223+
}
224+
servers, err := p.client.ListServers(ctx, projectID, region, labelSelector)
225+
if err != nil {
226+
return nil, fmt.Errorf("SDK ListServers with labelSelector: %v failed: %w", labelSelector, err)
227+
}
228+
229+
if len(servers) > 1 {
230+
return nil, fmt.Errorf("%v servers found for server name %v", len(servers), serverName)
231+
}
232+
233+
if len(servers) == 1 {
234+
return servers[0], nil
235+
}
236+
237+
// no servers found len == 0
238+
return nil, nil
239+
}
240+
241+
func (p *Provider) patchNetworkInterface(ctx context.Context, projectID, serverID string, providerSpec *api.ProviderSpec) error {
242+
if len(providerSpec.AllowedAddresses) == 0 {
243+
return nil
244+
}
245+
246+
nics, err := p.client.GetNICsForServer(ctx, projectID, providerSpec.Region, serverID)
247+
if err != nil {
248+
return fmt.Errorf("failed to get NICs for server %q: %w", serverID, err)
249+
}
250+
251+
if len(nics) == 0 {
252+
return fmt.Errorf("failed to find NIC for server %q", serverID)
253+
}
254+
255+
for _, nic := range nics {
256+
// if networking is not set, server is inside the default network
257+
// just patch the interface since the server should only have one
258+
if providerSpec.Networking != nil {
259+
// only process interfaces that are either in the configured network (NetworkID) or are defined in NICIDs
260+
if providerSpec.Networking.NetworkID != nic.NetworkID && !slices.Contains(providerSpec.Networking.NICIDs, nic.ID) {
261+
continue
262+
}
263+
}
264+
265+
updateNic := false
266+
// check if every cidr in providerspec.allowedAddresses is inside the nic allowedAddresses
267+
for _, allowedAddress := range providerSpec.AllowedAddresses {
268+
if !slices.Contains(nic.AllowedAddresses, allowedAddress) {
269+
nic.AllowedAddresses = append(nic.AllowedAddresses, allowedAddress)
270+
updateNic = true
271+
}
272+
}
273+
274+
if !updateNic {
275+
continue
276+
}
277+
278+
if _, err := p.client.UpdateNIC(ctx, projectID, providerSpec.Region, nic.NetworkID, nic.ID, nic.AllowedAddresses); err != nil {
279+
return fmt.Errorf("failed to update allowed addresses for NIC %s: %w", nic.ID, err)
280+
}
281+
282+
klog.V(2).Infof("Updated allowed addresses for NIC %s to %v", nic.ID, nic.AllowedAddresses)
283+
}
284+
285+
return nil
286+
}
287+
206288
// DeleteMachine handles a machine deletion request by deleting the STACKIT server
207289
//
208290
// This method deletes the server identified by the ProviderID from STACKIT infrastructure.
@@ -216,33 +298,52 @@ func (p *Provider) DeleteMachine(ctx context.Context, req *driver.DeleteMachineR
216298
klog.V(2).Infof("Machine deletion request has been received for %q", req.Machine.Name)
217299
defer klog.V(2).Infof("Machine deletion request has been processed for %q", req.Machine.Name)
218300

219-
// Validate ProviderID exists
220-
if req.Machine.Spec.ProviderID == "" {
221-
return nil, status.Error(codes.InvalidArgument, "ProviderID is required")
222-
}
223-
224301
// Extract credentials from Secret
225302
serviceAccountKey := string(req.Secret.Data["serviceaccount.json"])
226-
227303
// Initialize client on first use (lazy initialization)
228304
if err := p.ensureClient(serviceAccountKey); err != nil {
229305
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to initialize STACKIT client: %v", err))
230306
}
231307

232-
// Parse ProviderID to extract projectID and serverID
233-
projectID, serverID, err := parseProviderID(req.Machine.Spec.ProviderID)
308+
var projectID, serverID string
309+
var err error
310+
if req.Machine.Spec.ProviderID != "" {
311+
if !strings.HasPrefix(req.Machine.Spec.ProviderID, StackitProviderName) {
312+
return nil, status.Error(codes.InvalidArgument, "providerID is not empty and does not start with stackit://")
313+
}
314+
315+
// Parse ProviderID to extract projectID and serverID
316+
projectID, serverID, err = parseProviderID(req.Machine.Spec.ProviderID)
317+
if err != nil {
318+
klog.V(2).Infof("invalid ProviderID format: %v", err)
319+
}
320+
}
321+
234322
if projectID == "" {
235323
projectID = string(req.Secret.Data["project-id"])
236324
}
237-
if err != nil {
238-
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid ProviderID format: %v", err))
239-
}
240325

241326
providerSpec, err := decodeProviderSpec(req.MachineClass)
242327
if err != nil {
243328
return nil, status.Error(codes.Internal, err.Error())
244329
}
245330

331+
if serverID == "" {
332+
server, err := p.getServerByName(ctx, projectID, providerSpec.Region, req.Machine.Name)
333+
if err != nil {
334+
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to find server by name: %v", err))
335+
}
336+
337+
if server != nil {
338+
serverID = server.ID
339+
}
340+
}
341+
342+
if serverID == "" {
343+
klog.V(2).Infof("Server is already deleted for machine %q", req.Machine.Name)
344+
return &driver.DeleteMachineResponse{}, nil
345+
}
346+
246347
// Call STACKIT API to delete server
247348
err = p.client.DeleteServer(ctx, projectID, providerSpec.Region, serverID)
248349
if err != nil {
@@ -364,7 +465,9 @@ func (p *Provider) ListMachines(ctx context.Context, req *driver.ListMachinesReq
364465
}
365466

366467
// Call STACKIT API to list all servers
367-
labelSelector := fmt.Sprintf("%s=%s", StackitMachineClassLabel, req.MachineClass.Name)
468+
labelSelector := map[string]string{
469+
StackitMachineClassLabel: req.MachineClass.Name,
470+
}
368471
servers, err := p.client.ListServers(ctx, projectID, providerSpec.Region, labelSelector)
369472
if err != nil {
370473
klog.Errorf("Failed to list servers for MachineClass %q: %v", req.MachineClass.Name, err)

pkg/provider/core_create_machine_basic_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ var _ = Describe("CreateMachine", func() {
184184
Expect(err).To(HaveOccurred())
185185
statusErr, ok := status.FromError(err)
186186
Expect(ok).To(BeTrue())
187-
Expect(statusErr.Code()).To(Equal(codes.Internal))
187+
Expect(statusErr.Code()).To(Equal(codes.Unavailable))
188188
})
189189
})
190190
})

0 commit comments

Comments
 (0)