Skip to content

Commit dfb2344

Browse files
authored
Add support for security groups (#14)
1 parent 23b7c1b commit dfb2344

File tree

7 files changed

+217
-21
lines changed

7 files changed

+217
-21
lines changed

api/v1alpha1/scalewaymachine_types.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const MachineFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/sm-pro
1111
// +kubebuilder:validation:XValidation:rule="has(self.rootVolume) == has(oldSelf.rootVolume)",message="rootVolume cannot be added or removed"
1212
// +kubebuilder:validation:XValidation:rule="has(self.publicNetwork) == has(oldSelf.publicNetwork)",message="publicNetwork cannot be added or removed"
1313
// +kubebuilder:validation:XValidation:rule="has(self.placementGroup) == has(oldSelf.placementGroup)",message="placementGroup cannot be added or removed"
14+
// +kubebuilder:validation:XValidation:rule="has(self.securityGroup) == has(oldSelf.securityGroup)",message="securityGroup cannot be added or removed"
1415
type ScalewayMachineSpec struct {
1516
// ProviderID must match the provider ID as seen on the node object corresponding to this machine.
1617
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
@@ -40,6 +41,11 @@ type ScalewayMachineSpec struct {
4041
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
4142
// +optional
4243
PlacementGroup *PlacementGroupSpec `json:"placementGroup,omitempty"`
44+
45+
// SecurityGroup allows attaching a Security Group to the instance.
46+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
47+
// +optional
48+
SecurityGroup *SecurityGroupSpec `json:"securityGroup,omitempty"`
4349
}
4450

4551
// RootVolumeSpec defines the characteristics of the system (root) volume.
@@ -72,11 +78,25 @@ type PublicNetworkSpec struct {
7278
EnableIPv6 *bool `json:"enableIPv6,omitempty"`
7379
}
7480

81+
// PlacementGroupSpec contains an ID or Name of an existing Placement Group.
7582
// +kubebuilder:validation:XValidation:rule="(has(self.id) ? 1 : 0) + (has(self.name) ? 1 : 0) == 1",message="exactly one of id or name must be set"
7683
type PlacementGroupSpec struct {
7784
// ID of the placement group.
85+
// +optional
7886
ID *string `json:"id,omitempty"`
7987
// Name of the placement group.
88+
// +optional
89+
Name *string `json:"name,omitempty"`
90+
}
91+
92+
// SecurityGroupSpec contains an ID or Name of an existing Security Group.
93+
// +kubebuilder:validation:XValidation:rule="(has(self.id) ? 1 : 0) + (has(self.name) ? 1 : 0) == 1",message="exactly one of id or name must be set"
94+
type SecurityGroupSpec struct {
95+
// ID of the security group.
96+
// +optional
97+
ID *string `json:"id,omitempty"`
98+
// +optional
99+
// Name of the security group.
80100
Name *string `json:"name,omitempty"`
81101
}
82102

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachines.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,22 @@ spec:
148148
x-kubernetes-validations:
149149
- message: Value is immutable
150150
rule: self == oldSelf
151+
securityGroup:
152+
description: SecurityGroup allows attaching a Security Group to the
153+
instance.
154+
properties:
155+
id:
156+
description: ID of the security group.
157+
type: string
158+
name:
159+
description: Name of the security group.
160+
type: string
161+
type: object
162+
x-kubernetes-validations:
163+
- message: Value is immutable
164+
rule: self == oldSelf
165+
- message: exactly one of id or name must be set
166+
rule: '(has(self.id) ? 1 : 0) + (has(self.name) ? 1 : 0) == 1'
151167
required:
152168
- commercialType
153169
- image
@@ -159,6 +175,8 @@ spec:
159175
rule: has(self.publicNetwork) == has(oldSelf.publicNetwork)
160176
- message: placementGroup cannot be added or removed
161177
rule: has(self.placementGroup) == has(oldSelf.placementGroup)
178+
- message: securityGroup cannot be added or removed
179+
rule: has(self.securityGroup) == has(oldSelf.securityGroup)
162180
status:
163181
description: ScalewayMachineStatus defines the observed state of ScalewayMachine.
164182
properties:

config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachinetemplates.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,23 @@ spec:
152152
x-kubernetes-validations:
153153
- message: Value is immutable
154154
rule: self == oldSelf
155+
securityGroup:
156+
description: SecurityGroup allows attaching a Security Group
157+
to the instance.
158+
properties:
159+
id:
160+
description: ID of the security group.
161+
type: string
162+
name:
163+
description: Name of the security group.
164+
type: string
165+
type: object
166+
x-kubernetes-validations:
167+
- message: Value is immutable
168+
rule: self == oldSelf
169+
- message: exactly one of id or name must be set
170+
rule: '(has(self.id) ? 1 : 0) + (has(self.name) ? 1 : 0)
171+
== 1'
155172
required:
156173
- commercialType
157174
- image
@@ -163,6 +180,8 @@ spec:
163180
rule: has(self.publicNetwork) == has(oldSelf.publicNetwork)
164181
- message: placementGroup cannot be added or removed
165182
rule: has(self.placementGroup) == has(oldSelf.placementGroup)
183+
- message: securityGroup cannot be added or removed
184+
rule: has(self.securityGroup) == has(oldSelf.securityGroup)
166185
required:
167186
- spec
168187
type: object

docs/scalewaymachine.md

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,19 @@ Private Network (`network.privateNetwork.enabled`) in the `ScalewayCluster`.
172172

173173
## Placement Group
174174

175-
It is possible to attach an existing Placement group to the Instance server that will be created.
175+
It is possible to attach an existing placement group to the Instance server that will be created.
176176

177177
> [!WARNING]
178-
> A Placement group can be attached to at most 20 Instance servers.
178+
> A placement group can be attached to at most 20 Instance servers.
179179

180180
Placement groups allow you to define if you want certain Instances to run on
181181
different physical hypervisors for maximum availability or as physically close
182-
together as possible for minimum latency. For more information about Placement
182+
together as possible for minimum latency. For more information about placement
183183
groups, please refer to the [Scaleway documentation](https://www.scaleway.com/en/docs/instances/how-to/use-placement-groups/).
184184

185185
The `placementGroup` field must contain one of the following:
186186

187-
- A Placement group ID:
187+
- A placement group ID:
188188

189189
```yaml
190190
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
@@ -198,7 +198,7 @@ The `placementGroup` field must contain one of the following:
198198
# some fields were omitted...
199199
```
200200

201-
- A Placement group name:
201+
- A placement group name:
202202

203203
```yaml
204204
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
@@ -212,9 +212,55 @@ The `placementGroup` field must contain one of the following:
212212
# some fields were omitted...
213213
```
214214

215-
Make sure this Placement group exists in the zones where you plan to deploy your nodes.
216-
You can list Placement groups by name with this command:
215+
Make sure this placement group exists in the zones where you plan to deploy your nodes.
216+
You can list placement groups by name with this command:
217217

218218
```bash
219219
scw instance placement-group list name=${IMAGE_NAME} zone=${SCW_ZONE}
220220
```
221+
222+
## Security Group
223+
224+
It is possible to attach an existing security group to the Instance server that will be created.
225+
226+
Security groups act as firewalls, filtering public internet traffic on your Instances.
227+
They can be stateful or stateless, and allow you to create rules to drop or allow traffic
228+
to and from your Instance. For more information about security groups, please refer
229+
to the [Scaleway documentation](https://www.scaleway.com/en/docs/instances/how-to/use-security-groups/).
230+
231+
The `securityGroup` field must contain one of the following:
232+
233+
- A security group ID:
234+
235+
```yaml
236+
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
237+
kind: ScalewayMachine
238+
metadata:
239+
name: my-machine
240+
namespace: default
241+
spec:
242+
securityGroup:
243+
id: 11111111-1111-1111-1111-111111111111
244+
# some fields were omitted...
245+
```
246+
247+
- A security group name:
248+
249+
```yaml
250+
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
251+
kind: ScalewayMachine
252+
metadata:
253+
name: my-machine
254+
namespace: default
255+
spec:
256+
securityGroup:
257+
name: my-placement-group
258+
# some fields were omitted...
259+
```
260+
261+
Make sure this security group exists in the zones where you plan to deploy your nodes.
262+
You can list security groups by name with this command:
263+
264+
```bash
265+
scw instance security-group list name=${IMAGE_NAME} zone=${SCW_ZONE}
266+
```

internal/service/scaleway/client/instance.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (c *Client) CreateServer(
5050
ctx context.Context,
5151
zone scw.Zone,
5252
name, commercialType, imageID string,
53-
placementGroupID *string,
53+
placementGroupID, securityGroupID *string,
5454
rootVolumeSize scw.Size,
5555
rootVolumeType instance.VolumeVolumeType,
5656
publicIPs, tags []string,
@@ -74,6 +74,7 @@ func (c *Client) CreateServer(
7474
DynamicIPRequired: scw.BoolPtr(false),
7575
Image: &imageID,
7676
PlacementGroup: placementGroupID,
77+
SecurityGroup: securityGroupID,
7778
Volumes: map[string]*instance.VolumeServerTemplate{
7879
"0": {
7980
Size: &rootVolumeSize,
@@ -399,3 +400,32 @@ func (c *Client) FindPlacementGroup(ctx context.Context, zone scw.Zone, name str
399400
return nil, fmt.Errorf("%w: found %d placement groups with name %s", ErrTooManyItemsFound, len(placementGroups), name)
400401
}
401402
}
403+
404+
func (c *Client) FindSecurityGroup(ctx context.Context, zone scw.Zone, name string) (*instance.SecurityGroup, error) {
405+
if err := c.validateZone(c.instance, zone); err != nil {
406+
return nil, err
407+
}
408+
409+
resp, err := c.instance.ListSecurityGroups(&instance.ListSecurityGroupsRequest{
410+
Zone: zone,
411+
Name: &name,
412+
Project: &c.projectID,
413+
}, scw.WithContext(ctx), scw.WithAllPages())
414+
if err != nil {
415+
return nil, newCallError("ListSecurityGroups", err)
416+
}
417+
418+
// Filter out all security groups that have the wrong name.
419+
securityGroups := slices.DeleteFunc(resp.SecurityGroups, func(sg *instance.SecurityGroup) bool {
420+
return sg.Name != name
421+
})
422+
423+
switch len(securityGroups) {
424+
case 0:
425+
return nil, ErrNoItemFound
426+
case 1:
427+
return securityGroups[0], nil
428+
default:
429+
return nil, fmt.Errorf("%w: found %d security groups with name %s", ErrTooManyItemsFound, len(securityGroups), name)
430+
}
431+
}

internal/service/scaleway/instance/instance.go

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -256,20 +256,14 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) {
256256
}
257257
}
258258

259-
// If user has specified a placement group, get its ID.
260-
var placementGroupID *string
261-
if s.ScalewayMachine.Spec.PlacementGroup != nil {
262-
switch pgref := s.ScalewayMachine.Spec.PlacementGroup; {
263-
case pgref.ID != nil:
264-
placementGroupID = pgref.ID
265-
case pgref.Name != nil:
266-
placementGroup, err := s.ScalewayClient.FindPlacementGroup(ctx, zone, *pgref.Name)
267-
if err != nil {
268-
return nil, fmt.Errorf("failed to find placement group: %w", err)
269-
}
259+
placementGroupID, err := s.placementGroupID(ctx, zone)
260+
if err != nil {
261+
return nil, err
262+
}
270263

271-
placementGroupID = &placementGroup.ID
272-
}
264+
securityGroupID, err := s.securityGroupID(ctx, zone)
265+
if err != nil {
266+
return nil, err
273267
}
274268

275269
// Finally, create the server.
@@ -280,6 +274,7 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) {
280274
s.ScalewayMachine.Spec.CommercialType,
281275
imageID,
282276
placementGroupID,
277+
securityGroupID,
283278
s.RootVolumeSize(),
284279
volumeType,
285280
publicIPs,
@@ -304,6 +299,44 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) {
304299
return server, nil
305300
}
306301

302+
func (s *Service) placementGroupID(ctx context.Context, zone scw.Zone) (*string, error) {
303+
// If user has specified a placement group, get its ID.
304+
if s.ScalewayMachine.Spec.PlacementGroup != nil {
305+
switch pgref := s.ScalewayMachine.Spec.PlacementGroup; {
306+
case pgref.ID != nil:
307+
return pgref.ID, nil
308+
case pgref.Name != nil:
309+
placementGroup, err := s.ScalewayClient.FindPlacementGroup(ctx, zone, *pgref.Name)
310+
if err != nil {
311+
return nil, fmt.Errorf("failed to find placement group: %w", err)
312+
}
313+
314+
return &placementGroup.ID, nil
315+
}
316+
}
317+
318+
return nil, nil
319+
}
320+
321+
func (s *Service) securityGroupID(ctx context.Context, zone scw.Zone) (*string, error) {
322+
// If user has specified a security group, get its ID.
323+
if s.ScalewayMachine.Spec.SecurityGroup != nil {
324+
switch sgref := s.ScalewayMachine.Spec.SecurityGroup; {
325+
case sgref.ID != nil:
326+
return sgref.ID, nil
327+
case sgref.Name != nil:
328+
securityGroup, err := s.ScalewayClient.FindSecurityGroup(ctx, zone, *sgref.Name)
329+
if err != nil {
330+
return nil, fmt.Errorf("failed to find security group: %w", err)
331+
}
332+
333+
return &securityGroup.ID, nil
334+
}
335+
}
336+
337+
return nil, nil
338+
}
339+
307340
func (s *Service) ensurePrivateNIC(ctx context.Context, server *instance.Server) ([]*ipam.IP, error) {
308341
if !s.HasPrivateNetwork() {
309342
return nil, nil

0 commit comments

Comments
 (0)