Skip to content

Commit 4c6005c

Browse files
✨ Ensure server is started (#86)
**What is the purpose of this pull request/Why do we need it?** If a server is provisioned but somehow not started, you'd have to start the server manually. This now happens automatically, so the controller checks if the server is powered on, and if not, is starts the server. **Description of changes:** Automatically start servers if they are powered off. **Checklist:** - [x] Documentation updated - [x] Unit Tests added - [x] Includes [emojis](https://github.com/kubernetes-sigs/kubebuilder-release-tools?tab=readme-ov-file#kubebuilder-project-versioning)
1 parent d11a872 commit 4c6005c

File tree

5 files changed

+132
-6
lines changed

5 files changed

+132
-6
lines changed

internal/ionoscloud/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ type Client interface {
3434
GetServer(ctx context.Context, datacenterID, serverID string) (*sdk.Server, error)
3535
// DeleteServer deletes the server that matches the provided serverID in the specified data center.
3636
DeleteServer(ctx context.Context, datacenterID, serverID string) (string, error)
37+
// StartServer starts the server that matches the provided serverID in the specified data center.
38+
// Returning the location and an error if starting the server fails.
39+
StartServer(ctx context.Context, datacenterID, serverID string) (string, error)
3740
// CreateLAN creates a new LAN with the provided properties in the specified data center, returning the request path.
3841
CreateLAN(ctx context.Context, datacenterID string, properties sdk.LanPropertiesPost) (string, error)
3942
// PatchLAN patches the LAN that matches lanID in the specified data center with the provided properties, returning the request location.

internal/ionoscloud/client/client.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ func (c *IonosCloudClient) DeleteServer(ctx context.Context, datacenterID, serve
155155
return "", errLocationHeaderEmpty
156156
}
157157

158+
// StartServer starts the server that matches the provided serverID in the specified data center.
159+
// Returning the location and an error if starting the server fails.
160+
func (c *IonosCloudClient) StartServer(ctx context.Context, datacenterID, serverID string) (string, error) {
161+
if datacenterID == "" {
162+
return "", errDatacenterIDIsEmpty
163+
}
164+
if serverID == "" {
165+
return "", errServerIDIsEmpty
166+
}
167+
req, err := c.API.ServersApi.
168+
DatacentersServersStartPost(ctx, datacenterID, serverID).
169+
Execute()
170+
if err != nil {
171+
return "", fmt.Errorf(apiCallErrWrapper, err)
172+
}
173+
if location := req.Header.Get(locationHeaderKey); location != "" {
174+
return location, nil
175+
}
176+
177+
return "", errLocationHeaderEmpty
178+
}
179+
158180
// CreateLAN creates a new LAN with the provided properties in the specified data center, returning the request location.
159181
func (c *IonosCloudClient) CreateLAN(ctx context.Context, datacenterID string, properties sdk.LanPropertiesPost,
160182
) (string, error) {

internal/ionoscloud/clienttest/mock_client.go

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

internal/service/cloud/server.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (s *Service) ReconcileServer(ctx context.Context, ms *scope.Machine) (reque
6565
if server != nil {
6666
// Server is available
6767

68-
if !s.isServerAvailable(server) {
68+
if !s.isServerAvailable(ms, server) {
6969
// server is still provisioning, checking again later
7070
return true, nil
7171
}
@@ -138,18 +138,20 @@ func (s *Service) FinalizeMachineProvisioning(_ context.Context, ms *scope.Machi
138138
return false, nil
139139
}
140140

141-
func (s *Service) isServerAvailable(server *sdk.Server) bool {
141+
func (s *Service) isServerAvailable(ms *scope.Machine, server *sdk.Server) bool {
142142
log := s.logger.WithName("isServerAvailable")
143143
if state := getState(server); !isAvailable(state) {
144144
log.Info("Server is not available yet", "state", state)
145145
return false
146146
}
147147

148-
// TODO ensure server is started
149148
if vmState := getVMState(server); !isRunning(vmState) {
150-
log.Info("Server is not running yet", "state", vmState)
151-
// TODO start server
152-
return false
149+
err := s.startServer(context.Background(), ms.DatacenterID(), *server.Id)
150+
if err != nil {
151+
log.Error(err, "Failed to start the server")
152+
return false
153+
}
154+
return true
153155
}
154156

155157
return true
@@ -225,6 +227,20 @@ func (s *Service) deleteServer(ctx context.Context, ms *scope.Machine, serverID
225227
return nil
226228
}
227229

230+
func (s *Service) startServer(ctx context.Context, datacenterID, serverID string) error {
231+
log := s.logger.WithName("startServer")
232+
233+
log.V(4).Info("Starting server", "serverID", serverID)
234+
requestLocation, err := s.ionosClient.StartServer(ctx, datacenterID, serverID)
235+
if err != nil {
236+
return fmt.Errorf("failed to request server start: %w", err)
237+
}
238+
239+
log.Info("Successfully requested for server start", "location", requestLocation)
240+
log.V(4).Info("Done starting server")
241+
return nil
242+
}
243+
228244
func (s *Service) getLatestServerCreationRequest(ctx context.Context, ms *scope.Machine) (*requestInfo, error) {
229245
return getMatchingRequest(
230246
ctx,

internal/service/cloud/server_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,29 @@ func (s *serverSuite) TestReconcileServerRequestDoneStateAvailable() {
104104
s.False(requeue)
105105
}
106106

107+
func (s *serverSuite) TestReconcileServerRequestDoneStateAvailableTurnedOff() {
108+
s.prepareReconcileServerRequestTest()
109+
s.mockGetServerCreationRequest().Return([]sdk.Request{s.examplePostRequest(sdk.RequestStatusDone)}, nil)
110+
s.mockListServers().Return(&sdk.Servers{Items: &[]sdk.Server{
111+
{
112+
Id: ptr.To(exampleServerID),
113+
Metadata: &sdk.DatacenterElementMetadata{
114+
State: ptr.To(sdk.Available),
115+
},
116+
Properties: &sdk.ServerProperties{
117+
Name: ptr.To(s.service.serverName(s.infraMachine)),
118+
VmState: ptr.To(sdk.Available),
119+
},
120+
},
121+
}}, nil).Once()
122+
123+
s.mockStartServer().Return("", nil)
124+
125+
requeue, err := s.service.ReconcileServer(s.ctx, s.machineScope)
126+
s.NoError(err)
127+
s.False(requeue)
128+
}
129+
107130
func (s *serverSuite) TestReconcileServerNoRequest() {
108131
s.prepareReconcileServerRequestTest()
109132
s.mockGetServerCreationRequest().Return([]sdk.Request{}, nil)
@@ -330,6 +353,10 @@ func (s *serverSuite) mockCreateServer() *clienttest.MockClient_CreateServer_Cal
330353
)
331354
}
332355

356+
func (s *serverSuite) mockStartServer() *clienttest.MockClient_StartServer_Call {
357+
return s.ionosClient.EXPECT().StartServer(s.ctx, s.machineScope.DatacenterID(), exampleServerID)
358+
}
359+
333360
func (s *serverSuite) mockListLANs() *clienttest.MockClient_ListLANs_Call {
334361
return s.ionosClient.EXPECT().ListLANs(s.ctx, s.machineScope.DatacenterID())
335362
}

0 commit comments

Comments
 (0)