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

Release Powerpipe v1.2.2 #716

Merged
merged 9 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/02-powerpipe-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -700,11 +700,11 @@ jobs:

- name: Fail if PR title does not match with version
run: |
if ${{ (steps.pr_title.outputs.PR_TITLE == env.VERSION) }} == 'true';then
if [[ "${{ steps.pr_title.outputs.PR_TITLE }}" == "Powerpipe ${{ env.VERSION }}" ]]; then
echo "Correct version"
else
echo "Incorrect version"
exit 1
echo "Incorrect version"
exit 1
fi

- name: Merge pull request to update brew formula
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## v1.2.2 [2025-02-05]
_Bug Fixes_
- When DashboardServer executes a dashboard, ensure search path prefix is respected. ([#717](https://github.com/turbot/powerpipe/issues/717))

## v1.2.1 [2025-02-04]
_Bug Fixes_
- Fix backend support if the database is specified by a connection string. ([#713](https://github.com/turbot/powerpipe/issues/713))
- Improve search path button config popover to handle narrower screens. ([#711](https://github.com/turbot/powerpipe/issues/711))
- Dashboard UI sending `changed_input` field at wrong level in `input_changed` event. ([#708](https://github.com/turbot/powerpipe/issues/708))

## v1.2.0 [2025-01-30]
_Whats new_
- Add support for `tailpipe` detections and detection benchmarks.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/spf13/viper v1.19.0
github.com/stevenle/topsort v0.2.0
github.com/turbot/go-kit v0.10.0-rc.0
github.com/turbot/pipe-fittings/v2 v2.0.0-rc.2
github.com/turbot/pipe-fittings/v2 v2.0.1-rc.0
github.com/turbot/steampipe-plugin-sdk/v5 v5.11.0
github.com/turbot/terraform-components v0.0.0-20231213122222-1f3526cab7a7 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -826,8 +826,8 @@ github.com/turbot/go-kit v0.10.0-rc.0 h1:kd+jp2ibbIV33Hc8SsMAN410Dl9Pz6SJ40axbKU
github.com/turbot/go-kit v0.10.0-rc.0/go.mod h1:fFQqR59I5z5JeeBLfK1PjSifn4Oprs3NiQx0CxeSJxs=
github.com/turbot/pipe-fittings v1.6.0 h1:sn4OJPQQR0fggKMNUuyvShJipZG/Ze5jR48Hl12Eokg=
github.com/turbot/pipe-fittings v1.6.0/go.mod h1:1nlRVh18QkYy9eq5pW9gpnoE2VgnQW0Y2zKzrH8Q4kI=
github.com/turbot/pipe-fittings/v2 v2.0.0-rc.2 h1:0nUVueTEbgPSqhff/6DCCgUaAjkZHvHdKOf/PtoeyRE=
github.com/turbot/pipe-fittings/v2 v2.0.0-rc.2/go.mod h1:4srBITqYs/VWpgpqjQmJejJ3akkPVRueCHQa9CiETr0=
github.com/turbot/pipe-fittings/v2 v2.0.1-rc.0 h1:z11uypxjxxoFfltbNPJFC3Ub5HwoVtViPFB28T1ZS0g=
github.com/turbot/pipe-fittings/v2 v2.0.1-rc.0/go.mod h1:4srBITqYs/VWpgpqjQmJejJ3akkPVRueCHQa9CiETr0=
github.com/turbot/pipes-sdk-go v0.9.1 h1:2yRojY2wymvJn6NQyE6A0EDFV267MNe+yDLxPVvsBwM=
github.com/turbot/pipes-sdk-go v0.9.1/go.mod h1:Mb+KhvqqEdRbz/6TSZc2QWDrMa5BN3E4Xw+gPt2TRkc=
github.com/turbot/steampipe-plugin-code v0.7.0 h1:SROYIo/TI/Q/YNfXK+sAIS71umypUFm1Uz851TmoJkM=
Expand Down
6 changes: 3 additions & 3 deletions internal/cmd/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func runModInstallCmd(cmd *cobra.Command, args []string) {

// if any mod names were passed as args, convert into formed mod names
installOpts := modinstaller.NewInstallOpts(workspaceMod, args...)
installOpts.PluginVersions = getPluginVersions(ctx)
installOpts.PluginVersions = getPluginVersions(ctx, workspaceMod)

installData, err := modinstaller.InstallWorkspaceDependencies(ctx, installOpts)
if err != nil {
Expand All @@ -162,8 +162,8 @@ func validateModArgs() error {
return localcmdconfig.ValidateDatabaseArg()
}

func getPluginVersions(ctx context.Context) *plugin.PluginVersionMap {
defaultDatabase, _, err := db_client.GetDefaultDatabaseConfig()
func getPluginVersions(ctx context.Context, workspaceMod *modconfig.Mod) *plugin.PluginVersionMap {
defaultDatabase, _, err := db_client.GetDefaultDatabaseConfig(workspaceMod)
if err != nil {
if !viper.GetBool(constants.ArgForce) {
error_helpers.ShowWarning("Could not connect to database - plugin validation will not be performed")
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func runServerCmd(cmd *cobra.Command, _ []string) {
// setup a new webSocket service
webSocket := melody.New()
// create the dashboardServer
dashboardServer, err := dashboardserver.NewServer(ctx, modInitData.Workspace, webSocket)
dashboardServer, err := dashboardserver.NewServer(ctx, modInitData, webSocket)
error_helpers.FailOnError(err)

// send it over to the powerpipe API Server
Expand Down
21 changes: 14 additions & 7 deletions internal/dashboardexecute/dashboard_execution_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ type DashboardExecutionTree struct {
DateTimeRange utils.TimeRange
}

func newDashboardExecutionTree(rootResource modconfig.ModTreeItem, sessionId string, workspace *workspace.PowerpipeWorkspace, inputs *InputValues, defaultClientMap *db_client.ClientMap, opts ...backend.BackendOption) (*DashboardExecutionTree, error) {
func (e *DashboardExecutor) newDashboardExecutionTree(rootResource modconfig.ModTreeItem, sessionId string, workspace *workspace.PowerpipeWorkspace, inputs *InputValues, opts ...backend.BackendOption) (*DashboardExecutionTree, error) {
// now populate the DashboardExecutionTree
executionTree := &DashboardExecutionTree{
dashboardName: rootResource.Name(),
sessionId: sessionId,
defaultClientMap: defaultClientMap,
defaultClientMap: e.defaultClient,
clientMap: db_client.NewClientMap(),
runs: make(map[string]dashboardtypes.DashboardTreeRun),
workspace: workspace,
Expand All @@ -63,12 +63,19 @@ func newDashboardExecutionTree(rootResource modconfig.ModTreeItem, sessionId str
}
executionTree.id = fmt.Sprintf("%p", executionTree)

// set the dashboard database and search patch config
defaultDatabase, defaultSearchPathConfig, err := db_client.GetDefaultDatabaseConfig(opts...)
if err != nil {
return nil, err
// apply options and use to override the default search path
var cfg backend.BackendConfig
for _, opt := range opts {
opt(&cfg)
}
database, searchPathConfig, err := db_client.GetDatabaseConfigForResource(rootResource, workspace.Mod, defaultDatabase, defaultSearchPathConfig)

// has the search path been overridden?
defaultSearchPathConfig := e.defaultSearchPathConfig
if !cfg.SearchPathConfig.Empty() {
defaultSearchPathConfig = cfg.SearchPathConfig
}

database, searchPathConfig, err := db_client.GetDatabaseConfigForResource(rootResource, workspace.Mod, e.defaultDatabase, defaultSearchPathConfig)
if err != nil {
return nil, err
}
Expand Down
15 changes: 10 additions & 5 deletions internal/dashboardexecute/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

filehelpers "github.com/turbot/go-kit/files"
"github.com/turbot/pipe-fittings/v2/backend"
"github.com/turbot/pipe-fittings/v2/connection"
"github.com/turbot/pipe-fittings/v2/modconfig"
"github.com/turbot/pipe-fittings/v2/utils"
"github.com/turbot/powerpipe/internal/dashboardevents"
Expand All @@ -29,15 +30,19 @@ type DashboardExecutor struct {
interactive bool
// store the default client which is created during initData creation
// - this is to avoid creating a new client for each dashboard execution if the database/search path is NOT overridden
defaultClient *db_client.ClientMap
defaultClient *db_client.ClientMap
defaultDatabase connection.ConnectionStringProvider
defaultSearchPathConfig backend.SearchPathConfig
}

func NewDashboardExecutor(defaultClient *db_client.ClientMap) *DashboardExecutor {
func NewDashboardExecutor(defaultClient *db_client.ClientMap, defaultDatabase connection.ConnectionStringProvider, defaultSearchPathConfig backend.SearchPathConfig) *DashboardExecutor {
return &DashboardExecutor{
executions: make(map[string]*DashboardExecutionTree),
// default to interactive execution
interactive: true,
defaultClient: defaultClient,
interactive: true,
defaultClient: defaultClient,
defaultDatabase: defaultDatabase,
defaultSearchPathConfig: defaultSearchPathConfig,
}
}

Expand Down Expand Up @@ -68,7 +73,7 @@ func (e *DashboardExecutor) ExecuteDashboard(ctx context.Context, sessionId stri
e.CancelExecutionForSession(ctx, sessionId)

// now create a new execution
executionTree, err = newDashboardExecutionTree(rootResource, sessionId, workspace, inputs, e.defaultClient, opts...)
executionTree, err = e.newDashboardExecutionTree(rootResource, sessionId, workspace, inputs, opts...)
if err != nil {
return err
}
Expand Down
110 changes: 110 additions & 0 deletions internal/dashboardserver/backend_support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package dashboardserver

import (
"context"
"log/slog"

"github.com/turbot/pipe-fittings/v2/backend"
"github.com/turbot/pipe-fittings/v2/connection"
"github.com/turbot/pipe-fittings/v2/constants"
"github.com/turbot/pipe-fittings/v2/modconfig"
)

type backendSupport struct {
supportsSearchPath bool
supportsTimeRange bool
}

// setFromDb sets the backend support based on the database type
func (bs *backendSupport) setFromDb(db connection.ConnectionStringProvider) {
if db != nil {
switch db.(type) {
case *connection.SteampipePgConnection, *connection.PostgresConnection:
bs.supportsSearchPath = true
case *connection.TailpipeConnection:
bs.supportsTimeRange = true
case *connection.ConnectionString:
// create a backend and get its name
// if it is a steampipe or postgres backend, set supportsSearchPath
// NOTE: a tailpipe connection cannot be specified by a connection string
// (as the connection string is dynamic and provided by the Tailpipe CLI),
// so we will not set the supportsTimeRange flag here

// we do not expect an error from ConnectionString.GetConnectionString
connectionString, _ := db.GetConnectionString()
// get the backend name from the connection string
// NOTE: this does not create the backend and will therefore return postgres for a steampipe backend
// as we cannot tell the difference purely from the connection string
// This is fine as we just want to determine whether a search path is supported
backendName, _ := backend.NameFromConnectionString(context.Background(), connectionString)
// set supportsSearchPath if the backend is a steampipe or postgres backend
bs.supportsSearchPath = backendName == constants.PostgresBackendName
}
}
}

func newBackendSupport(database connection.ConnectionStringProvider) *backendSupport {
bs := &backendSupport{}
bs.setFromDb(database)
return bs
}

// determineBackendSupport determines the backend support for a dashboard
// if no resource has a specified database, use the default database to set the backend support
func determineBackendSupport(dashboard modconfig.ModTreeItem, defaultDatabase connection.ConnectionStringProvider) backendSupport {
bs := determineBackendSupportForResource(dashboard)
if bs == nil {
slog.Info("determineBackendSupport - no resource in the tree specifies a database, using default database")
bs = newBackendSupport(defaultDatabase)
}

slog.Info("determineBackendSupport", "supportsSearchPath", bs.supportsSearchPath, "supportsTimeRange", bs.supportsTimeRange)
return *bs
}

func determineBackendSupportForResource(item modconfig.ModTreeItem) *backendSupport {
var bs *backendSupport

// NOT: just check the database on this resource - GetDatabase also checks the parents
// - there is no need to do that here as we are traversing down the tree
if db := item.GetModTreeItemImpl().Database; db != nil {
bs = &backendSupport{}
bs.setFromDb(db)
slog.Info("determineBackendSupportForResource - resource has database", "resource", item.Name(), "backendSupport", bs)
}

// if we have now set both flags, we can stop - no need to traverse further
if backendSupportsAll(bs) {
return bs
}

for _, child := range item.GetChildren() {
childBs := determineBackendSupportForResource(child)
// merge this with out ba
bs = mergeBackendSupport(bs, childBs)

// if we have now set both flags, we can stop - no need to traverse further
if backendSupportsAll(bs) {
return bs
}
}
return bs
}

func backendSupportsAll(bs *backendSupport) bool {
return bs != nil && bs.supportsSearchPath && bs.supportsTimeRange
}

// merge 2 backend support objects
func mergeBackendSupport(bs1, bs2 *backendSupport) *backendSupport {
if bs1 == nil {
return bs2
}
if bs2 == nil {
return bs1
}
return &backendSupport{
supportsSearchPath: bs1.supportsSearchPath || bs2.supportsSearchPath,
supportsTimeRange: bs1.supportsTimeRange || bs2.supportsTimeRange,
}
}
74 changes: 7 additions & 67 deletions internal/dashboardserver/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/spf13/viper"
"log/slog"

"github.com/spf13/viper"
typeHelpers "github.com/turbot/go-kit/types"
"github.com/turbot/pipe-fittings/v2/app_specific"
"github.com/turbot/pipe-fittings/v2/backend"
"github.com/turbot/pipe-fittings/v2/connection"
"github.com/turbot/pipe-fittings/v2/constants"
"github.com/turbot/pipe-fittings/v2/modconfig"
"github.com/turbot/pipe-fittings/v2/steampipeconfig"
Expand All @@ -23,7 +22,7 @@ import (
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
)

func buildServerMetadataPayload(rm modconfig.ModResources, pipesMetadata *steampipeconfig.PipesMetadata) ([]byte, error) {
func (s *Server) buildServerMetadataPayload(rm modconfig.ModResources, pipesMetadata *steampipeconfig.PipesMetadata) ([]byte, error) {
workspaceResources := rm.(*resources.PowerpipeModResources)
installedMods := make(map[string]*ModMetadata)
for _, mod := range workspaceResources.Mods {
Expand Down Expand Up @@ -52,14 +51,8 @@ func buildServerMetadataPayload(rm modconfig.ModResources, pipesMetadata *steamp
cliVersion = versionFile.Version
}

defaultDatabase, defaultSearchPathConfig, err := db_client.GetDefaultDatabaseConfig()
if err != nil {
return nil, err
}

// populate the backend support flags (supportsSearchPath, supportsTimeRange) from the default database
var bs backendSupport
bs.setFromDb(defaultDatabase)
bs := newBackendSupport(s.defaultDatabase)

payload := ServerMetadataPayload{
Action: "server_metadata",
Expand All @@ -74,11 +67,11 @@ func buildServerMetadataPayload(rm modconfig.ModResources, pipesMetadata *steamp
},
}

connectionString, err := defaultDatabase.GetConnectionString()
connectionString, err := s.defaultDatabase.GetConnectionString()
if err != nil {
return nil, err
}
searchPath, err := getSearchPathMetadata(context.Background(), connectionString, defaultSearchPathConfig)
searchPath, err := getSearchPathMetadata(context.Background(), connectionString, s.defaultSearchPathConfig)
if err != nil {
return nil, err
}
Expand All @@ -99,18 +92,12 @@ func buildServerMetadataPayload(rm modconfig.ModResources, pipesMetadata *steamp
return json.Marshal(payload)
}

func buildDashboardMetadataPayload(dashboard modconfig.ModTreeItem) ([]byte, error) {
func (s *Server) buildDashboardMetadataPayload(dashboard modconfig.ModTreeItem) ([]byte, error) {
slog.Debug("calling buildDashboardMetadataPayload")

defaultDatabase, _, err := db_client.GetDefaultDatabaseConfig()
if err != nil {
slog.Warn("error getting database config for resource", "error", err)
return nil, err
}

// walk the tree of resources and determine whether any of them are using a tailpipe/steampipe/postrgres
// and set the SupportsSearchPath and SupportsTimeRange flags accordingly
backendSupport := determineBackendSupport(dashboard, defaultDatabase)
backendSupport := determineBackendSupport(dashboard, s.defaultDatabase)

payload := DashboardMetadataPayload{
Action: "dashboard_metadata",
Expand All @@ -128,53 +115,6 @@ func buildDashboardMetadataPayload(dashboard modconfig.ModTreeItem) ([]byte, err
return res, nil
}

type backendSupport struct {
supportsSearchPath bool
supportsTimeRange bool
}

func (bs *backendSupport) setFromDb(db connection.ConnectionStringProvider) {
if db != nil {
switch db.(type) {
case *connection.SteampipePgConnection, *connection.PostgresConnection:
bs.supportsSearchPath = true
case *connection.TailpipeConnection:
bs.supportsTimeRange = true
}
}
}
func determineBackendSupport(dashboard modconfig.ModTreeItem, defaultDatabase connection.ConnectionStringProvider) backendSupport {
var res backendSupport

usingDefaultDb := determineBackendSupportForResource(dashboard, &res)
if usingDefaultDb {
res.setFromDb(defaultDatabase)
}
return res
}

func determineBackendSupportForResource(item modconfig.ModTreeItem, bs *backendSupport) bool {
var usingDefaultDb bool
db := item.GetDatabase()
if db == nil {
usingDefaultDb = true
} else {
bs.setFromDb(db)
}

// if we have now found both, we can stop
if bs.supportsSearchPath && bs.supportsTimeRange {
return false
}
for _, child := range item.GetChildren() {
childUsingDefaultDb := determineBackendSupportForResource(child, bs)
if childUsingDefaultDb {
usingDefaultDb = true
}
}
return usingDefaultDb
}

func getSearchPathMetadata(ctx context.Context, database string, searchPathConfig backend.SearchPathConfig) (*SearchPathMetadata, error) {
// if backend supports search path, get it
client, err := db_client.NewClientMap().GetOrCreate(ctx, database, searchPathConfig)
Expand Down
Loading
Loading