Files
steampipe/pkg/cloud/snapshot.go

146 lines
4.3 KiB
Go

package cloud
import (
"context"
"fmt"
"log"
"path"
"strings"
"github.com/spf13/viper"
steampipecloud "github.com/turbot/steampipe-cloud-sdk-go"
"github.com/turbot/steampipe/pkg/constants"
"github.com/turbot/steampipe/pkg/dashboard/dashboardtypes"
"github.com/turbot/steampipe/pkg/export"
"github.com/turbot/steampipe/pkg/steampipeconfig"
"github.com/turbot/steampipe/sperr"
)
func PublishSnapshot(ctx context.Context, snapshot *dashboardtypes.SteampipeSnapshot, share bool) (string, error) {
snapshotLocation := viper.GetString(constants.ArgSnapshotLocation)
// snapshotLocation must be set (validation should ensure this)
if snapshotLocation == "" {
return "", sperr.New("to share a snapshot, snapshot-location must be set")
}
// if snapshot location is a workspace handle, upload it
if steampipeconfig.IsCloudWorkspaceIdentifier(snapshotLocation) {
url, err := uploadSnapshot(ctx, snapshot, share)
if err != nil {
return "", sperr.Wrap(err)
}
return fmt.Sprintf("\nSnapshot uploaded to %s\n", url), nil
}
// otherwise assume snapshot location is a file path
filePath, err := exportSnapshot(snapshot)
if err != nil {
return "", sperr.Wrap(err)
}
return fmt.Sprintf("\nSnapshot saved to %s\n", filePath), nil
}
func exportSnapshot(snapshot *dashboardtypes.SteampipeSnapshot) (string, error) {
exporter := &export.SnapshotExporter{}
fileName := export.GenerateDefaultExportFileName(snapshot.FileNameRoot, exporter.FileExtension())
dirName := viper.GetString(constants.ArgSnapshotLocation)
filePath := path.Join(dirName, fileName)
err := exporter.Export(context.Background(), snapshot, filePath)
if err != nil {
return "", sperr.Wrap(err)
}
return filePath, nil
}
func uploadSnapshot(ctx context.Context, snapshot *dashboardtypes.SteampipeSnapshot, share bool) (string, error) {
client := newSteampipeCloudClient(viper.GetString(constants.ArgCloudToken))
cloudWorkspace := viper.GetString(constants.ArgSnapshotLocation)
parts := strings.Split(cloudWorkspace, "/")
if len(parts) != 2 {
return "", sperr.New("failed to resolve username and workspace handle from workspace %s", cloudWorkspace)
}
identityHandle := parts[0]
workspaceHandle := parts[1]
// no determine whether this is a user or org workspace
// get the identity
identity, _, err := client.Identities.Get(ctx, identityHandle).Execute()
if err != nil {
return "", sperr.Wrap(err)
}
workspaceType := identity.Type
// set the visibility
visibility := "workspace"
if share {
visibility = "anyone_with_link"
}
// resolve the snapshot title
title := resolveSnapshotTitle(snapshot)
log.Printf("[TRACE] Uploading snapshot with title %s", title)
// populate map of tags tags been set?
tags := getTags()
cloudSnapshot, err := snapshot.AsCloudSnapshot()
if err != nil {
return "", sperr.Wrap(err)
}
// strip verbose/sensitive fields
dashboardtypes.StripSnapshot(cloudSnapshot)
req := steampipecloud.CreateWorkspaceSnapshotRequest{Data: *cloudSnapshot, Tags: tags, Visibility: &visibility}
req.SetTitle(title)
var uploadedSnapshot steampipecloud.WorkspaceSnapshot
if identity.Type == "user" {
uploadedSnapshot, _, err = client.UserWorkspaceSnapshots.Create(ctx, identityHandle, workspaceHandle).Request(req).Execute()
} else {
uploadedSnapshot, _, err = client.OrgWorkspaceSnapshots.Create(ctx, identityHandle, workspaceHandle).Request(req).Execute()
}
if err != nil {
return "", sperr.Wrap(err)
}
snapshotId := uploadedSnapshot.Id
snapshotUrl := fmt.Sprintf("https://%s/%s/%s/workspace/%s/snapshot/%s",
viper.GetString(constants.ArgCloudHost),
workspaceType,
identityHandle,
workspaceHandle,
snapshotId)
return snapshotUrl, nil
}
func resolveSnapshotTitle(snapshot *dashboardtypes.SteampipeSnapshot) string {
if titleArg := viper.GetString(constants.ArgSnapshotTitle); titleArg != "" {
return titleArg
}
// is there a title property set on the snapshot
if snapshotTitle := snapshot.Title; snapshotTitle != "" {
return snapshotTitle
}
// fall back to the fully qualified name of the root resource (which is also the FileNameRoot)
return snapshot.FileNameRoot
}
func getTags() map[string]any {
tags := viper.GetStringSlice(constants.ArgSnapshotTag)
res := map[string]any{}
for _, tagStr := range tags {
parts := strings.Split(tagStr, "=")
if len(parts) != 2 {
continue
}
res[parts[0]] = parts[1]
}
return res
}