Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1570. Connect Action #1638

Merged
merged 29 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9bd0120
#1570. Fix vite config for dev server by setting server.fs.strict to …
mike-winberry Apr 21, 2023
f1a88ff
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry Apr 24, 2023
e633d9d
Update deployed-package-menu by extracting the logic for removing a p…
mike-winberry Apr 24, 2023
5f4c0c6
#1570. Update api/packages/list.go with ListPackageConnections metho…
mike-winberry Apr 26, 2023
f15fdc8
#1570. Update deployed-package-menu connect list item to have the pro…
mike-winberry Apr 26, 2023
6412b7b
#1570. Create api/cluster tunnels.go that contains the ConnectTunnel …
mike-winberry Apr 26, 2023
11450dc
#1570. Fix api/cluster tunnels.go with correct typing for tunnels map…
mike-winberry May 1, 2023
e1208d2
Update internal/cluster tunnel.go with new FullUrl public method that…
mike-winberry May 1, 2023
a0dceda
#1570. Update api/tunnels tunnels.go with the tunnels package name. U…
mike-winberry May 1, 2023
10b2e06
#1570. Create ui DisconnectDeployedPackageDialog that shows a list of…
mike-winberry May 1, 2023
9437b3c
1570. Update workflows test-ui with the connect tests. Update interna…
mike-winberry May 1, 2023
7b107ad
#1570. Rename test/ui 04_connect_games to 04_connect_doom. Fix test/u…
mike-winberry May 1, 2023
73441a9
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry May 1, 2023
80d4758
1570 Fix api tunnels.go package comment
mike-winberry May 1, 2023
07e19c6
Fix api tunnels.go ListTunnels unused variable r
mike-winberry May 1, 2023
3c66078
Fix api tunnels.go renamed launchTunnelUrl to launchTunnelURL
mike-winberry May 1, 2023
90e83f8
Fix cluster tunnel.go renamed FullUrl to FullURL and updated all refe…
mike-winberry May 1, 2023
6c14e9c
Increase timeout to complete tests. Should be shorter after #1665
mike-winberry May 2, 2023
0374802
Update test/ui connect_doom to no longer wait for the disconnect call…
mike-winberry May 2, 2023
7926f94
#1570. Fix tunnels.go by removing the redundant makeTunnels() method …
mike-winberry May 2, 2023
a7b25b8
#1570. Update start.go by moving the tunnels routes under packages, u…
mike-winberry May 2, 2023
987d909
Removed 'secure' language from ui ConnectDeployedPackageDialog and Di…
mike-winberry May 3, 2023
33752af
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry May 3, 2023
922b270
Fix api/package tunnels linting errors
mike-winberry May 3, 2023
681cc4a
#1570. Moved the ConnectionStrings out of the MetaData object and add…
mike-winberry May 4, 2023
c0326f6
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry May 4, 2023
15b715c
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry May 4, 2023
4df8638
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
mike-winberry May 5, 2023
4761061
Merge branch 'main' into 1570-add-connect-action-to-package-actions-menu
Racer159 May 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ jobs:
export NODE_PATH=$(pwd)/src/ui/node_modules &&
npm --prefix src/ui run test:pre-init &&
npm --prefix src/ui run test:init &&
npm --prefix src/ui run test:post-init
npm --prefix src/ui run test:post-init &&
npm --prefix src/ui run test:connect

- name: Save logs
if: always()
Expand Down
112 changes: 112 additions & 0 deletions src/internal/api/packages/tunnels.go
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)
}
6 changes: 5 additions & 1 deletion src/internal/api/start.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package api provides the UI API server.
Expand Down Expand Up @@ -93,12 +94,15 @@ func LaunchAPIServer() {
r.Put("/deploy", packages.DeployPackage)
r.Get("/deploy-stream", packages.StreamDeployPackage)
r.Delete("/remove/{name}", packages.RemovePackage)
r.Put("/{pkg}/connect/{name}", packages.ConnectTunnel)
r.Delete("/{pkg}/disconnect/{name}", packages.DisconnectTunnel)
r.Get("/{pkg}/connections", packages.ListPackageConnections)
r.Get("/connections", packages.ListConnections)
})

r.Route("/components", func(r chi.Router) {
r.Get("/deployed", components.ListDeployingComponents)
})

})

// If no dev port specified, use the server port for the URL and try to open it
Expand Down
5 changes: 5 additions & 0 deletions src/internal/cluster/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ func (tunnel *Tunnel) HTTPEndpoint() string {
return fmt.Sprintf("http://%s", tunnel.Endpoint())
}

// FullURL returns the tunnel endpoint as a HTTP URL string with the urlSuffix appended.
func (tunnel *Tunnel) FullURL() string {
return fmt.Sprintf("%s%s", tunnel.HTTPEndpoint(), tunnel.urlSuffix)
}

// Close disconnects a tunnel connection by closing the StopChan, thereby stopping the goroutine.
func (tunnel *Tunnel) Close() {
message.Debug("tunnel.Close()")
Expand Down
3 changes: 2 additions & 1 deletion src/internal/cluster/zarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (c *Cluster) StripZarfLabelsAndSecretsFromNamespaces() {
}

// RecordPackageDeployment saves metadata about a package that has been deployed to the cluster.
func (c *Cluster) RecordPackageDeployment(pkg types.ZarfPackage, components []types.DeployedComponent) {
func (c *Cluster) RecordPackageDeployment(pkg types.ZarfPackage, components []types.DeployedComponent, connectStrings types.ConnectStrings) {
// Generate a secret that describes the package that is being deployed
packageName := pkg.Metadata.Name
deployedPackageSecret := c.Kube.GenerateSecret(ZarfNamespaceName, config.ZarfPackagePrefix+packageName, corev1.SecretTypeOpaque)
Expand All @@ -108,6 +108,7 @@ func (c *Cluster) RecordPackageDeployment(pkg types.ZarfPackage, components []ty
CLIVersion: config.CLIVersion,
Data: pkg,
DeployedComponents: components,
ConnectStrings: connectStrings,
})

deployedPackageSecret.Data = map[string][]byte{"data": stateData}
Expand Down
3 changes: 2 additions & 1 deletion src/pkg/packager/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,13 @@ func (p *Packager) Deploy() error {

// Notify all the things about the successful deployment
message.Successf("Zarf deployment complete")

p.printTablesForDeployment(deployedComponents)

// Save deployed package information to k8s
// Note: Not all packages need k8s; check if k8s is being used before saving the secret
if p.cluster != nil {
p.cluster.RecordPackageDeployment(p.cfg.Pkg, deployedComponents)
p.cluster.RecordPackageDeployment(p.cfg.Pkg, deployedComponents, connectStrings)
}

return nil
Expand Down
77 changes: 77 additions & 0 deletions src/test/ui/04_connect_doom.spec.ts
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;
}
41 changes: 29 additions & 12 deletions src/types/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@
// Package types contains all the types used by Zarf.
package types

import "k8s.io/client-go/tools/clientcmd/api"
import (
"k8s.io/client-go/tools/clientcmd/api"
)

// RestAPI is the struct that is used to marshal/unmarshal the top-level API objects.
type RestAPI struct {
ZarfPackage ZarfPackage `json:"zarfPackage"`
ZarfState ZarfState `json:"zarfState"`
ZarfCommonOptions ZarfCommonOptions `json:"zarfCommonOptions"`
ZarfCreateOptions ZarfCreateOptions `json:"zarfCreateOptions"`
ZarfDeployOptions ZarfDeployOptions `json:"zarfDeployOptions"`
ZarfInitOptions ZarfInitOptions `json:"zarfInitOptions"`
ConnectStrings ConnectStrings `json:"connectStrings"`
ClusterSummary ClusterSummary `json:"clusterSummary"`
DeployedPackage DeployedPackage `json:"deployedPackage"`
APIZarfPackage APIZarfPackage `json:"apiZarfPackage"`
APIZarfDeployPayload APIZarfDeployPayload `json:"apiZarfDeployPayload"`
ZarfPackage ZarfPackage `json:"zarfPackage"`
ZarfState ZarfState `json:"zarfState"`
ZarfCommonOptions ZarfCommonOptions `json:"zarfCommonOptions"`
ZarfCreateOptions ZarfCreateOptions `json:"zarfCreateOptions"`
ZarfDeployOptions ZarfDeployOptions `json:"zarfDeployOptions"`
ZarfInitOptions ZarfInitOptions `json:"zarfInitOptions"`
ConnectStrings ConnectStrings `json:"connectStrings"`
ClusterSummary ClusterSummary `json:"clusterSummary"`
DeployedPackage DeployedPackage `json:"deployedPackage"`
APIZarfPackage APIZarfPackage `json:"apiZarfPackage"`
APIZarfDeployPayload APIZarfDeployPayload `json:"apiZarfDeployPayload"`
APIZarfPackageConnection APIDeployedPackageConnection `json:"apiZarfPackageConnection"`
APIDeployedPackageConnections APIDeployedPackageConnections `json:"apiZarfPackageConnections"`
APIConnections APIConnections `json:"apiConnections"`
}

// ClusterSummary contains the summary of a cluster for the API.
Expand All @@ -42,3 +47,15 @@ type APIZarfDeployPayload struct {
DeployOpts ZarfDeployOptions `json:"deployOpts"`
InitOpts *ZarfInitOptions `json:"initOpts,omitempty"`
}

// APIConnections represents all of the existing connections
type APIConnections map[string]APIDeployedPackageConnections

// APIDeployedPackageConnections represents all of the connections for a deployed package
type APIDeployedPackageConnections []APIDeployedPackageConnection

// APIDeployedPackageConnection represents a single connection from a deployed package
type APIDeployedPackageConnection struct {
Name string `json:"name"`
URL string `json:"url,omitempty"`
}
1 change: 1 addition & 0 deletions src/types/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type DeployedPackage struct {
CLIVersion string `json:"cliVersion"`

DeployedComponents []DeployedComponent `json:"deployedComponents"`
ConnectStrings ConnectStrings `json:"connectStrings,omitempty"`
}

// DeployedComponent contains information about a Zarf Package Component that has been deployed to a cluster.
Expand Down
Loading