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

Execute bundle from a pre-computed Run #3051

Merged
merged 6 commits into from
Apr 29, 2024
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
80 changes: 27 additions & 53 deletions pkg/cnab/provider/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"errors"
"fmt"
"sort"

"get.porter.sh/porter/pkg/cnab"
"get.porter.sh/porter/pkg/config"
Expand All @@ -24,14 +23,14 @@ type HostVolumeMountSpec struct {
ReadOnly bool
}

// Shared arguments for all CNAB actions
// ActionArguments are the shared arguments for all bundle runs.
type ActionArguments struct {
// Action to execute, e.g. install, upgrade.
Action string

// Name of the installation.
Installation storage.Installation

// Run defines how to execute the bundle.
Run storage.Run

// BundleReference is the set of information necessary to execute a bundle.
BundleReference cnab.BundleReference

Expand All @@ -40,6 +39,7 @@ type ActionArguments struct {
Files map[string]string

// Params is the fully resolved set of parameters.
// TODO(PEP003): This should be removed in https://github.com/getporter/porter/issues/2699
Params map[string]interface{}

// Driver is the CNAB-compliant driver used to run bundle actions.
Expand Down Expand Up @@ -145,56 +145,57 @@ func (r *Runtime) Execute(ctx context.Context, args ActionArguments) error {
case <-ctx.Done():
return ctx.Err()
default:
currentRun := args.Run
ctx, log := tracing.StartSpan(ctx,
attribute.String("action", args.Action),
attribute.String("action", currentRun.Action),
attribute.Bool("allowDockerHostAccess", args.AllowDockerHostAccess),
attribute.String("driver", args.Driver))
defer log.EndSpan()
args.BundleReference.AddToTrace(ctx)
args.Installation.AddToTrace(ctx)

if args.Action == "" {
if currentRun.Action == "" {
return log.Error(errors.New("action is required"))
}

b, err := r.ProcessBundle(ctx, args.BundleReference.Definition)
if err != nil {
return log.Error(err)
}

currentRun, err := r.CreateRun(ctx, args, b)
if err != nil {
return log.Error(err)
return err
}

// Validate the action
if _, err := b.GetAction(currentRun.Action); err != nil {
return log.Error(fmt.Errorf("invalid action '%s' specified for bundle %s: %w", currentRun.Action, b.Name, err))
}

creds, err := r.loadCredentials(ctx, b, args)
if err != nil {
return log.Error(fmt.Errorf("not load credentials: %w", err))
return log.Errorf("invalid action '%s' specified for bundle %s: %w", currentRun.Action, b.Name, err)
}

log.Debugf("Using runtime driver %s\n", args.Driver)
driver, err := r.newDriver(args.Driver, args)
if err != nil {
return log.Error(fmt.Errorf("unable to instantiate driver: %w", err))
return log.Errorf("unable to instantiate driver: %w", err)
}

a := cnabaction.New(driver)
a.SaveLogs = args.PersistLogs

// Resolve parameters and credentials just-in-time (JIT) before running the bundle, do this at the *LAST* possible moment
log.Info("Just-in-time resolving credentials...")
if err = r.loadCredentials(ctx, b, &currentRun); err != nil {
return log.Errorf("could not resolve credentials before running the bundle: %w", err)
}
log.Info("Just-in-time resolving parameters...")
if err = r.loadParameters(ctx, b, &currentRun); err != nil {
return log.Errorf("could not resolve parameters before running the bundle: %w", err)
}

if currentRun.ShouldRecord() {
err = r.SaveRun(ctx, args.Installation, currentRun, cnab.StatusRunning)
if err != nil {
return log.Error(fmt.Errorf("could not save the pending action's status, the bundle was not executed: %w", err))
return log.Errorf("could not save the pending action's status, the bundle was not executed: %w", err)
}
}

cnabClaim := currentRun.ToCNAB()
cnabCreds := creds.ToCNAB()
cnabCreds := currentRun.Credentials.ToCNAB()
// The claim and credentials contain sensitive values. Only trace it in special dev builds (nothing is traced for release builds)
log.SetSensitiveAttributes(
tracing.ObjectAttribute("cnab-claim", cnabClaim),
Expand All @@ -204,46 +205,19 @@ func (r *Runtime) Execute(ctx context.Context, args ActionArguments) error {
if currentRun.ShouldRecord() {
if err != nil {
err = r.appendFailedResult(ctx, err, currentRun)
return log.Error(fmt.Errorf("failed to record that %s for installation %s failed: %w", args.Action, args.Installation.Name, err))
return log.Errorf("failed to record that %s for installation %s failed: %w", currentRun.Action, args.Installation.Name, err)
}
return r.SaveOperationResult(ctx, opResult, args.Installation, currentRun, currentRun.NewResultFrom(result))
}

if err != nil {
return log.Error(fmt.Errorf("execution of %s for installation %s failed: %w", args.Action, args.Installation.Name, err))
return log.Errorf("execution of %s for installation %s failed: %w", currentRun.Action, args.Installation.Name, err)
}

return nil
}
}

func (r *Runtime) CreateRun(ctx context.Context, args ActionArguments, b cnab.ExtendedBundle) (storage.Run, error) {
ctx, span := tracing.StartSpan(ctx)
defer span.EndSpan()

// Create a record for the run we are about to execute
var currentRun = args.Installation.NewRun(args.Action, b)
currentRun.Bundle = b.Bundle
currentRun.BundleReference = args.BundleReference.Reference.String()
currentRun.BundleDigest = args.BundleReference.Digest.String()

var err error
extb := cnab.NewBundle(b.Bundle)
currentRun.Parameters.Parameters, err = r.sanitizer.CleanRawParameters(ctx, args.Params, extb, currentRun.ID)
if err != nil {
return storage.Run{}, span.Error(err)
}

// TODO: Do not save secrets when the run isn't recorded
currentRun.ParameterOverrides = storage.LinkSensitiveParametersToSecrets(currentRun.ParameterOverrides, extb, currentRun.ID)
currentRun.CredentialSets = args.Installation.CredentialSets
sort.Strings(currentRun.CredentialSets)

currentRun.ParameterSets = args.Installation.ParameterSets
sort.Strings(currentRun.ParameterSets)
return currentRun, nil
}

// SaveRun with the specified status.
func (r *Runtime) SaveRun(ctx context.Context, installation storage.Installation, run storage.Run, status string) error {
ctx, span := tracing.StartSpan(ctx)
Expand All @@ -259,12 +233,12 @@ func (r *Runtime) SaveRun(ctx context.Context, installation storage.Installation
return span.Error(fmt.Errorf("error saving the installation record before executing the bundle: %w", err))
}

result := run.NewResult(status)
err = r.installations.InsertRun(ctx, run)
err = r.installations.UpsertRun(ctx, run)
if err != nil {
return span.Error(fmt.Errorf("error saving the installation run record before executing the bundle: %w", err))
}

result := run.NewResult(status)
err = r.installations.InsertResult(ctx, result)
if err != nil {
return span.Error(fmt.Errorf("error saving the installation status record before executing the bundle: %w", err))
Expand Down
56 changes: 19 additions & 37 deletions pkg/cnab/provider/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,34 @@ import (
"context"

"get.porter.sh/porter/pkg/cnab"
"get.porter.sh/porter/pkg/secrets"
"get.porter.sh/porter/pkg/storage"
"get.porter.sh/porter/pkg/tracing"
"go.mongodb.org/mongo-driver/bson"
)

func (r *Runtime) loadCredentials(ctx context.Context, b cnab.ExtendedBundle, args ActionArguments) (secrets.Set, error) {
func (r *Runtime) loadCredentials(ctx context.Context, b cnab.ExtendedBundle, run *storage.Run) error {
ctx, span := tracing.StartSpan(ctx)
defer span.EndSpan()

if len(args.Installation.CredentialSets) == 0 {
return nil, storage.Validate(nil, b.Credentials, args.Action)
resolvedCredentials, err := r.credentials.ResolveAll(ctx, run.Credentials)
if err != nil {
return span.Error(err)
}

// The strategy here is "last one wins". We loop through each credential file and
// calculate its credentials. Then we insert them into the creds map in the order
// in which they were supplied on the CLI.
resolvedCredentials := secrets.Set{}
for _, name := range args.Installation.CredentialSets {
var cset storage.CredentialSet
// Try to get the creds in the local namespace first, fallback to the global creds
query := storage.FindOptions{
Sort: []string{"-namespace"},
Filter: bson.M{
"name": name,
"$or": []bson.M{
{"namespace": ""},
{"namespace": args.Installation.Namespace},
},
},
}
store := r.credentials.GetDataStore()
err := store.FindOne(ctx, storage.CollectionCredentials, query, &cset)
if err != nil {
return nil, err
}

rc, err := r.credentials.ResolveAll(ctx, cset)
if err != nil {
return nil, err
}

for k, v := range rc {
resolvedCredentials[k] = v
}
for i, cred := range run.Credentials.Credentials {
run.Credentials.Credentials[i].ResolvedValue = resolvedCredentials[cred.Name]
}

return resolvedCredentials, storage.Validate(resolvedCredentials, b.Credentials, args.Action)
err = run.Credentials.ValidateBundle(b.Credentials, run.Action)
if err != nil {
return span.Error(err)
}

err = run.SetCredentialsDigest()
if err != nil {
// Just warn since the digest isn't critical for running the bundle
// If it's not set properly, we will recalculate as needed
span.Warnf("WARNING: unable to set the run's credentials digest: %w", err)
}

return nil
}
Loading
Loading