-
Notifications
You must be signed in to change notification settings - Fork 171
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description Ashton should be able to connect to a pre-defined connection within a deployed package. ## Related Issue #1570 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Other (security config, docs update, etc) ## Checklist before merging - [x] Test, docs, adr added or updated as needed - [x] [Contributor Guide Steps](https://github.com/defenseunicorns/zarf/blob/main/CONTRIBUTING.md#developer-workflow) followed --------- Co-authored-by: Wayne Starr <[email protected]>
- Loading branch information
1 parent
2a5adf2
commit 5fcdc3c
Showing
23 changed files
with
968 additions
and
4,362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors | ||
|
||
// Package packages provides api functions for managing Zarf packages. | ||
package packages | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
|
||
"github.com/defenseunicorns/zarf/src/internal/api/common" | ||
"github.com/defenseunicorns/zarf/src/internal/cluster" | ||
"github.com/defenseunicorns/zarf/src/pkg/message" | ||
"github.com/defenseunicorns/zarf/src/types" | ||
"github.com/go-chi/chi/v5" | ||
) | ||
|
||
// PackageTunnel is a struct for storing a tunnel and its connection details | ||
type PackageTunnel struct { | ||
tunnel *cluster.Tunnel | ||
Connection types.APIDeployedPackageConnection `json:"connection,omitempty"` | ||
} | ||
|
||
// packageTunnels is a map of package names to PackageTunnel objects | ||
type packageTunnels map[string]map[string]PackageTunnel | ||
|
||
// tunnels is a map of package names to tunnel objects used for storing connected tunnels | ||
var tunnels = make(packageTunnels) | ||
|
||
// ListConnections returns a map of pkgName to a list of connections | ||
func ListConnections(w http.ResponseWriter, _ *http.Request) { | ||
allConnections := make(types.APIConnections) | ||
for name, pkgTunnels := range tunnels { | ||
for _, pkgTunnel := range pkgTunnels { | ||
if allConnections[name] == nil { | ||
allConnections[name] = make(types.APIDeployedPackageConnections, 0) | ||
} | ||
allConnections[name] = append(allConnections[name], pkgTunnel.Connection) | ||
} | ||
} | ||
common.WriteJSONResponse(w, allConnections, http.StatusOK) | ||
} | ||
|
||
// ListPackageConnections lists all tunnel names | ||
func ListPackageConnections(w http.ResponseWriter, r *http.Request) { | ||
pkgName := chi.URLParam(r, "pkg") | ||
if tunnels[pkgName] == nil { | ||
message.ErrorWebf(errors.New("No tunnels for package %s"), w, pkgName) | ||
return | ||
} | ||
pkgTunnels := make(types.APIDeployedPackageConnections, 0, len(tunnels[pkgName])) | ||
for _, pkgTunnel := range tunnels[pkgName] { | ||
pkgTunnels = append(pkgTunnels, pkgTunnel.Connection) | ||
} | ||
|
||
common.WriteJSONResponse(w, pkgTunnels, http.StatusOK) | ||
} | ||
|
||
// ConnectTunnel establishes a tunnel for the requested resource | ||
func ConnectTunnel(w http.ResponseWriter, r *http.Request) { | ||
pkgName := chi.URLParam(r, "pkg") | ||
connectionName := chi.URLParam(r, "name") | ||
|
||
if tunnels[pkgName] == nil { | ||
tunnels[pkgName] = make(map[string]PackageTunnel) | ||
} | ||
|
||
pkgTunnels := tunnels[pkgName] | ||
|
||
if pkgTunnels[connectionName].tunnel != nil { | ||
common.WriteJSONResponse(w, tunnels[pkgName][connectionName].Connection, http.StatusOK) | ||
return | ||
} | ||
|
||
tunnel, err := cluster.NewZarfTunnel() | ||
|
||
if err != nil { | ||
message.ErrorWebf(err, w, "Failed to create tunnel for %s", connectionName) | ||
return | ||
} | ||
|
||
err = tunnel.Connect(connectionName, false) | ||
if err != nil { | ||
message.ErrorWebf(err, w, "Failed to connect to %s", connectionName) | ||
return | ||
} | ||
|
||
tunnels[pkgName][connectionName] = PackageTunnel{ | ||
tunnel: tunnel, | ||
Connection: types.APIDeployedPackageConnection{ | ||
Name: connectionName, | ||
URL: tunnel.FullURL(), | ||
}, | ||
} | ||
common.WriteJSONResponse(w, tunnels[pkgName][connectionName].Connection, http.StatusCreated) | ||
} | ||
|
||
// DisconnectTunnel closes the tunnel for the requested resource | ||
func DisconnectTunnel(w http.ResponseWriter, r *http.Request) { | ||
pkgName := chi.URLParam(r, "pkg") | ||
connectionName := chi.URLParam(r, "name") | ||
pkgTunnel := tunnels[pkgName][connectionName] | ||
if pkgTunnel.tunnel == nil { | ||
message.ErrorWebf(errors.New("Tunnel not found"), w, "Failed to disconnect from %s", connectionName) | ||
return | ||
} | ||
|
||
pkgTunnel.tunnel.Close() | ||
delete(tunnels[pkgName], connectionName) | ||
|
||
common.WriteJSONResponse(w, true, http.StatusOK) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors | ||
|
||
import { expect, test } from '@playwright/test'; | ||
|
||
test.describe.serial('connect the dos-games package @connect', async () => { | ||
test.beforeEach(async ({ page }) => { | ||
page.on('pageerror', (err) => console.log(err.message)); | ||
await page.goto('/auth?token=insecure', { waitUntil: 'networkidle' }); | ||
}); | ||
|
||
test('connect the dos-games package', async ({ page }) => { | ||
let menu = await openDosGamesMenu(page); | ||
|
||
// Ensure the menu contains the Connect option | ||
expect(await menu.textContent()).toContain('Connect...'); | ||
|
||
const connect = menu.locator('span:text-is("Connect...")').first(); | ||
|
||
// Open Connect Deployed Package Dialog | ||
await connect.click(); | ||
|
||
const connectDialog = page.locator('.dialog-open'); | ||
expect(await connectDialog.textContent()).toContain('Connect to Resource'); | ||
const connectButton = connectDialog.locator('button:has-text("Connect")'); | ||
|
||
// Click the Connect Button | ||
await Promise.all([ | ||
page.waitForResponse('api/packages/dos-games/connect/doom'), | ||
connectButton.click(), | ||
]); | ||
|
||
menu = await openDosGamesMenu(page); | ||
expect(await menu.textContent()).toContain('Disconnect...'); | ||
}); | ||
|
||
test('disconnect the dos-games package', async ({ page }) => { | ||
// Dispose context once it's no longer needed. | ||
let menu = await openDosGamesMenu(page); | ||
|
||
// Ensure the menu contains the Disconnect option | ||
expect(await menu.textContent()).toContain('Disconnect...'); | ||
|
||
const disconnect = menu.locator('span:text-is("Disconnect...")'); | ||
|
||
// Open Disconnect Deployed Package Dialog | ||
await disconnect.click(); | ||
|
||
const dialog = page.locator('.dialog-open'); | ||
expect(await dialog.textContent()).toContain('Disconnect Resource'); | ||
const disconnectButton = dialog.locator('.button-label:text-is("Disconnect")'); | ||
|
||
// Click the Disconnect Button | ||
await Promise.all([ | ||
page.waitForResponse('api/packages/dos-games/disconnect/doom'), | ||
disconnectButton.click(), | ||
]); | ||
|
||
// Ensure the menu no longer contains the Disconnect option | ||
menu = await openDosGamesMenu(page); | ||
expect(await menu.textContent()).not.toContain('Disconnect...'); | ||
}); | ||
}); | ||
|
||
async function openDosGamesMenu(page: any) { | ||
// Find Dos Games Package in Packages Table | ||
const packageTableBody = page.locator('.package-list-body'); | ||
const packageRow = packageTableBody.locator('.package-table-row:has-text("dos-games")'); | ||
|
||
// Open the menu for the package | ||
const more = packageRow.locator('.more > button').first(); | ||
await more.click(); | ||
|
||
// Find the menu and return it | ||
const menu = page.locator('.menu.open'); | ||
return menu; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.