Skip to content

Commit dee63a5

Browse files
authored
Create dedicated endpoints for app installation and status (#2624)
* Create dedicated endpoints for app installation and status
1 parent 7fbfdce commit dee63a5

File tree

29 files changed

+1653
-158
lines changed

29 files changed

+1653
-158
lines changed

api/client/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ type Client interface {
2121
TemplateLinuxAppConfig(values types.AppConfigValues) (types.AppConfig, error)
2222
RunLinuxAppPreflights() (types.InstallAppPreflightsStatusResponse, error)
2323
GetLinuxAppPreflightsStatus() (types.InstallAppPreflightsStatusResponse, error)
24+
InstallLinuxApp() (types.AppInstall, error)
25+
GetLinuxAppInstallStatus() (types.AppInstall, error)
2426

2527
GetKubernetesInstallationConfig() (types.KubernetesInstallationConfig, error)
2628
ConfigureKubernetesInstallation(config types.KubernetesInstallationConfig) (types.Status, error)
@@ -32,6 +34,8 @@ type Client interface {
3234
TemplateKubernetesAppConfig(values types.AppConfigValues) (types.AppConfig, error)
3335
RunKubernetesAppPreflights() (types.InstallAppPreflightsStatusResponse, error)
3436
GetKubernetesAppPreflightsStatus() (types.InstallAppPreflightsStatusResponse, error)
37+
InstallKubernetesApp() (types.AppInstall, error)
38+
GetKubernetesAppInstallStatus() (types.AppInstall, error)
3539
}
3640

3741
type client struct {

api/client/client_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,3 +1214,181 @@ func TestGetKubernetesAppPreflightsStatus(t *testing.T) {
12141214
assert.Equal(t, http.StatusInternalServerError, apiErr.StatusCode)
12151215
assert.Equal(t, "Internal Server Error", apiErr.Message)
12161216
}
1217+
1218+
func TestClient_InstallLinuxApp(t *testing.T) {
1219+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1220+
assert.Equal(t, "POST", r.Method)
1221+
assert.Equal(t, "/api/linux/install/app/install", r.URL.Path)
1222+
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
1223+
1224+
appInstall := types.AppInstall{
1225+
Status: types.Status{State: types.StateRunning, Description: "Installing app"},
1226+
Logs: "Installation started\n",
1227+
}
1228+
w.Header().Set("Content-Type", "application/json")
1229+
json.NewEncoder(w).Encode(appInstall)
1230+
}))
1231+
defer server.Close()
1232+
1233+
c := New(server.URL, WithToken("test-token"))
1234+
appInstall, err := c.InstallLinuxApp()
1235+
1236+
require.NoError(t, err)
1237+
assert.Equal(t, types.StateRunning, appInstall.Status.State)
1238+
assert.Equal(t, "Installing app", appInstall.Status.Description)
1239+
assert.Equal(t, "Installation started\n", appInstall.Logs)
1240+
}
1241+
1242+
func TestClient_GetLinuxAppInstallStatus(t *testing.T) {
1243+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1244+
assert.Equal(t, "GET", r.Method)
1245+
assert.Equal(t, "/api/linux/install/app/status", r.URL.Path)
1246+
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
1247+
1248+
appInstall := types.AppInstall{
1249+
Status: types.Status{State: types.StateSucceeded, Description: "App installed successfully"},
1250+
Logs: "Installation completed\n",
1251+
}
1252+
w.Header().Set("Content-Type", "application/json")
1253+
json.NewEncoder(w).Encode(appInstall)
1254+
}))
1255+
defer server.Close()
1256+
1257+
c := New(server.URL, WithToken("test-token"))
1258+
appInstall, err := c.GetLinuxAppInstallStatus()
1259+
1260+
require.NoError(t, err)
1261+
assert.Equal(t, types.StateSucceeded, appInstall.Status.State)
1262+
assert.Equal(t, "App installed successfully", appInstall.Status.Description)
1263+
assert.Equal(t, "Installation completed\n", appInstall.Logs)
1264+
}
1265+
1266+
func TestClient_InstallKubernetesApp(t *testing.T) {
1267+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1268+
assert.Equal(t, "POST", r.Method)
1269+
assert.Equal(t, "/api/kubernetes/install/app/install", r.URL.Path)
1270+
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
1271+
1272+
appInstall := types.AppInstall{
1273+
Status: types.Status{State: types.StateRunning, Description: "Installing app"},
1274+
Logs: "Kubernetes app installation started\n",
1275+
}
1276+
w.Header().Set("Content-Type", "application/json")
1277+
json.NewEncoder(w).Encode(appInstall)
1278+
}))
1279+
defer server.Close()
1280+
1281+
c := New(server.URL, WithToken("test-token"))
1282+
appInstall, err := c.InstallKubernetesApp()
1283+
1284+
require.NoError(t, err)
1285+
assert.Equal(t, types.StateRunning, appInstall.Status.State)
1286+
assert.Equal(t, "Installing app", appInstall.Status.Description)
1287+
assert.Equal(t, "Kubernetes app installation started\n", appInstall.Logs)
1288+
}
1289+
1290+
func TestClient_GetKubernetesAppInstallStatus(t *testing.T) {
1291+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1292+
assert.Equal(t, "GET", r.Method)
1293+
assert.Equal(t, "/api/kubernetes/install/app/status", r.URL.Path)
1294+
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
1295+
1296+
appInstall := types.AppInstall{
1297+
Status: types.Status{State: types.StateFailed, Description: "App installation failed"},
1298+
Logs: "Installation failed with error\n",
1299+
}
1300+
w.Header().Set("Content-Type", "application/json")
1301+
json.NewEncoder(w).Encode(appInstall)
1302+
}))
1303+
defer server.Close()
1304+
1305+
c := New(server.URL, WithToken("test-token"))
1306+
appInstall, err := c.GetKubernetesAppInstallStatus()
1307+
1308+
require.NoError(t, err)
1309+
assert.Equal(t, types.StateFailed, appInstall.Status.State)
1310+
assert.Equal(t, "App installation failed", appInstall.Status.Description)
1311+
assert.Equal(t, "Installation failed with error\n", appInstall.Logs)
1312+
}
1313+
1314+
func TestClient_AppInstallErrorHandling(t *testing.T) {
1315+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1316+
apiError := types.APIError{
1317+
StatusCode: http.StatusInternalServerError,
1318+
Message: "Internal server error",
1319+
}
1320+
w.Header().Set("Content-Type", "application/json")
1321+
w.WriteHeader(http.StatusInternalServerError)
1322+
json.NewEncoder(w).Encode(apiError)
1323+
}))
1324+
defer server.Close()
1325+
1326+
c := New(server.URL, WithToken("test-token"))
1327+
1328+
t.Run("InstallLinuxApp error", func(t *testing.T) {
1329+
_, err := c.InstallLinuxApp()
1330+
require.Error(t, err)
1331+
apiErr, ok := err.(*types.APIError)
1332+
require.True(t, ok)
1333+
assert.Equal(t, http.StatusInternalServerError, apiErr.StatusCode)
1334+
assert.Equal(t, "Internal server error", apiErr.Message)
1335+
})
1336+
1337+
t.Run("GetLinuxAppInstallStatus error", func(t *testing.T) {
1338+
_, err := c.GetLinuxAppInstallStatus()
1339+
require.Error(t, err)
1340+
apiErr, ok := err.(*types.APIError)
1341+
require.True(t, ok)
1342+
assert.Equal(t, http.StatusInternalServerError, apiErr.StatusCode)
1343+
})
1344+
1345+
t.Run("InstallKubernetesApp error", func(t *testing.T) {
1346+
_, err := c.InstallKubernetesApp()
1347+
require.Error(t, err)
1348+
apiErr, ok := err.(*types.APIError)
1349+
require.True(t, ok)
1350+
assert.Equal(t, http.StatusInternalServerError, apiErr.StatusCode)
1351+
})
1352+
1353+
t.Run("GetKubernetesAppInstallStatus error", func(t *testing.T) {
1354+
_, err := c.GetKubernetesAppInstallStatus()
1355+
require.Error(t, err)
1356+
apiErr, ok := err.(*types.APIError)
1357+
require.True(t, ok)
1358+
assert.Equal(t, http.StatusInternalServerError, apiErr.StatusCode)
1359+
})
1360+
}
1361+
1362+
func TestClient_AppInstallWithoutToken(t *testing.T) {
1363+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1364+
// Verify no auth header is sent
1365+
assert.Empty(t, r.Header.Get("Authorization"))
1366+
1367+
apiError := types.APIError{
1368+
StatusCode: http.StatusUnauthorized,
1369+
Message: "Unauthorized",
1370+
}
1371+
w.Header().Set("Content-Type", "application/json")
1372+
w.WriteHeader(http.StatusUnauthorized)
1373+
json.NewEncoder(w).Encode(apiError)
1374+
}))
1375+
defer server.Close()
1376+
1377+
c := New(server.URL) // No token provided
1378+
1379+
t.Run("InstallLinuxApp without token", func(t *testing.T) {
1380+
_, err := c.InstallLinuxApp()
1381+
require.Error(t, err)
1382+
apiErr, ok := err.(*types.APIError)
1383+
require.True(t, ok)
1384+
assert.Equal(t, http.StatusUnauthorized, apiErr.StatusCode)
1385+
})
1386+
1387+
t.Run("GetLinuxAppInstallStatus without token", func(t *testing.T) {
1388+
_, err := c.GetLinuxAppInstallStatus()
1389+
require.Error(t, err)
1390+
apiErr, ok := err.(*types.APIError)
1391+
require.True(t, ok)
1392+
assert.Equal(t, http.StatusUnauthorized, apiErr.StatusCode)
1393+
})
1394+
}

api/client/install.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,3 +599,111 @@ func (c *client) GetKubernetesAppPreflightsStatus() (types.InstallAppPreflightsS
599599

600600
return status, nil
601601
}
602+
603+
func (c *client) InstallLinuxApp() (types.AppInstall, error) {
604+
req, err := http.NewRequest("POST", c.apiURL+"/api/linux/install/app/install", nil)
605+
if err != nil {
606+
return types.AppInstall{}, err
607+
}
608+
req.Header.Set("Content-Type", "application/json")
609+
setAuthorizationHeader(req, c.token)
610+
611+
resp, err := c.httpClient.Do(req)
612+
if err != nil {
613+
return types.AppInstall{}, err
614+
}
615+
defer resp.Body.Close()
616+
617+
if resp.StatusCode != http.StatusOK {
618+
return types.AppInstall{}, errorFromResponse(resp)
619+
}
620+
621+
var appInstall types.AppInstall
622+
err = json.NewDecoder(resp.Body).Decode(&appInstall)
623+
if err != nil {
624+
return types.AppInstall{}, err
625+
}
626+
627+
return appInstall, nil
628+
}
629+
630+
func (c *client) GetLinuxAppInstallStatus() (types.AppInstall, error) {
631+
req, err := http.NewRequest("GET", c.apiURL+"/api/linux/install/app/status", nil)
632+
if err != nil {
633+
return types.AppInstall{}, err
634+
}
635+
req.Header.Set("Content-Type", "application/json")
636+
setAuthorizationHeader(req, c.token)
637+
638+
resp, err := c.httpClient.Do(req)
639+
if err != nil {
640+
return types.AppInstall{}, err
641+
}
642+
defer resp.Body.Close()
643+
644+
if resp.StatusCode != http.StatusOK {
645+
return types.AppInstall{}, errorFromResponse(resp)
646+
}
647+
648+
var appInstall types.AppInstall
649+
err = json.NewDecoder(resp.Body).Decode(&appInstall)
650+
if err != nil {
651+
return types.AppInstall{}, err
652+
}
653+
654+
return appInstall, nil
655+
}
656+
657+
func (c *client) InstallKubernetesApp() (types.AppInstall, error) {
658+
req, err := http.NewRequest("POST", c.apiURL+"/api/kubernetes/install/app/install", nil)
659+
if err != nil {
660+
return types.AppInstall{}, err
661+
}
662+
req.Header.Set("Content-Type", "application/json")
663+
setAuthorizationHeader(req, c.token)
664+
665+
resp, err := c.httpClient.Do(req)
666+
if err != nil {
667+
return types.AppInstall{}, err
668+
}
669+
defer resp.Body.Close()
670+
671+
if resp.StatusCode != http.StatusOK {
672+
return types.AppInstall{}, errorFromResponse(resp)
673+
}
674+
675+
var appInstall types.AppInstall
676+
err = json.NewDecoder(resp.Body).Decode(&appInstall)
677+
if err != nil {
678+
return types.AppInstall{}, err
679+
}
680+
681+
return appInstall, nil
682+
}
683+
684+
func (c *client) GetKubernetesAppInstallStatus() (types.AppInstall, error) {
685+
req, err := http.NewRequest("GET", c.apiURL+"/api/kubernetes/install/app/status", nil)
686+
if err != nil {
687+
return types.AppInstall{}, err
688+
}
689+
req.Header.Set("Content-Type", "application/json")
690+
setAuthorizationHeader(req, c.token)
691+
692+
resp, err := c.httpClient.Do(req)
693+
if err != nil {
694+
return types.AppInstall{}, err
695+
}
696+
defer resp.Body.Close()
697+
698+
if resp.StatusCode != http.StatusOK {
699+
return types.AppInstall{}, errorFromResponse(resp)
700+
}
701+
702+
var appInstall types.AppInstall
703+
err = json.NewDecoder(resp.Body).Decode(&appInstall)
704+
if err != nil {
705+
return types.AppInstall{}, err
706+
}
707+
708+
return appInstall, nil
709+
}

api/controllers/app/install/controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type Controller interface {
2626
GetAppPreflightOutput(ctx context.Context) (*types.PreflightsOutput, error)
2727
GetAppPreflightTitles(ctx context.Context) ([]string, error)
2828
InstallApp(ctx context.Context) error
29+
GetAppInstallStatus(ctx context.Context) (types.AppInstall, error)
2930
}
3031

3132
var _ Controller = (*InstallController)(nil)

api/controllers/app/install/controller_mock.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,9 @@ func (m *MockController) InstallApp(ctx context.Context) error {
7373
args := m.Called(ctx)
7474
return args.Error(0)
7575
}
76+
77+
// GetAppInstallStatus mocks the GetAppInstallStatus method
78+
func (m *MockController) GetAppInstallStatus(ctx context.Context) (types.AppInstall, error) {
79+
args := m.Called(ctx)
80+
return args.Get(0).(types.AppInstall), args.Error(1)
81+
}

0 commit comments

Comments
 (0)