Skip to content

Commit

Permalink
Consolidate logic for cnab provider
Browse files Browse the repository at this point in the history
ONE FUNCTION TO RULE THEM ALL!
  • Loading branch information
carolynvs-msft committed Jun 15, 2020
1 parent c0d5cbf commit 487403a
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 288 deletions.
101 changes: 101 additions & 0 deletions pkg/cnab/provider/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"encoding/json"
"fmt"

cnabaction "github.com/cnabio/cnab-go/action"
"github.com/cnabio/cnab-go/bundle"

"get.porter.sh/porter/pkg/config"
"github.com/cnabio/cnab-go/action"
"github.com/cnabio/cnab-go/claim"
Expand Down Expand Up @@ -106,6 +109,104 @@ func (r *Runtime) AddRelocation(args ActionArguments) action.OperationConfigFunc
}
}

func (r *Runtime) ExecuteAction(action string, args ActionArguments) error {
var b bundle.Bundle
var err error

if args.BundlePath != "" {
b, err = r.ProcessBundle(args.BundlePath)
if err != nil {
return err
}
}

existingClaim, err := r.claims.ReadLastClaim(args.Instance)
if err != nil {
// Only install and stateless actions can execute without an initial installation
if !(action == claim.ActionInstall || b.Actions[action].Stateless) {
return errors.Wrapf(err, "could not load bundle instance %s", args.Instance)
}
}

// If the user didn't override the bundle definition, use the one
// from the existing claim
if existingClaim.ID != "" && args.BundlePath == "" {
b = existingClaim.Bundle
}

params, err := r.loadParameters(b, args.Params, args.ParameterSets, action)
if err != nil {
return errors.Wrap(err, "invalid parameters")
}

var c claim.Claim
if existingClaim.ID == "" {
c, err = claim.New(args.Instance, action, b, params)
} else {
c, err = existingClaim.NewClaim(action, b, params)
}
if err != nil {
return err
}

// Validate the action we are about to perform
err = c.Validate()
if err != nil {
return err
}

creds, err := r.loadCredentials(c.Bundle, args.CredentialIdentifiers)
if err != nil {
return errors.Wrap(err, "could not load credentials")
}

driver, err := r.newDriver(args.Driver, args.Instance, args)
if err != nil {
return errors.Wrap(err, "unable to instantiate driver")
}

a := cnabaction.New(driver, r.claims)
a.SaveAllOutputs = true

modifies, err := c.IsModifyingAction()
if err != nil {
return err
}

// Only record runs that modify the bundle, e.g. don't save "logs" or "dry-run"
// In theory a custom action shouldn't ever have modifies AND stateless
// (which creates a temp claim) but just in case, if it does modify, we must
// persist.
shouldPersistClaim := func() bool {
stateless := false
if customAction, ok := c.Bundle.Actions[action]; ok {
stateless = customAction.Stateless
}
return modifies && !stateless
}()

if shouldPersistClaim {
err = a.SaveInitialClaim(c, claim.StatusRunning)
if err != nil {
return err
}
}

r.printDebugInfo(creds, params)

opResult, result, err := a.Run(c, creds, r.ApplyConfig(args)...)

if shouldPersistClaim {
if err != nil {
err = r.appendFailedResult(err, c)
return errors.Wrapf(err, "failed to %s the bundle", action)
}
return a.SaveOperationResult(opResult, c, result)
} else {
return errors.Wrapf(err, "failed to %s the bundle", action)
}
}

// appendFailedResult creates a failed result from the operation error and accumulates
// the error(s).
func (r *Runtime) appendFailedResult(opErr error, c claim.Claim) error {
Expand Down
45 changes: 1 addition & 44 deletions pkg/cnab/provider/install.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,9 @@
package cnabprovider

import (
"github.com/cnabio/cnab-go/action"
"github.com/cnabio/cnab-go/claim"
"github.com/pkg/errors"
)

func (r *Runtime) Install(args ActionArguments) error {
b, err := r.ProcessBundle(args.BundlePath)
if err != nil {
return err
}

params, err := r.loadParameters(b, args.Params, args.ParameterSets, claim.ActionInstall)
if err != nil {
return errors.Wrap(err, "invalid parameters")
}

c, err := claim.New(args.Instance, claim.ActionInstall, b, params)
if err != nil {
return errors.Wrap(err, "invalid bundle instance name")
}

creds, err := r.loadCredentials(b, args.CredentialIdentifiers)
if err != nil {
return errors.Wrap(err, "could not load credentials")
}

driver, err := r.newDriver(args.Driver, c.Installation, args)
if err != nil {
return errors.Wrap(err, "unable to instantiate driver")
}

a := action.New(driver, r.claims)
a.SaveAllOutputs = true

err = a.SaveInitialClaim(c, claim.StatusRunning)
if err != nil {
return err
}

r.printDebugInfo(creds, params)

opResult, result, err := a.Run(c, creds, r.ApplyConfig(args)...)
if err != nil {
err = r.appendFailedResult(err, c)
return errors.Wrap(err, "failed to install the bundle")
}

return a.SaveOperationResult(opResult, c, result)
return r.ExecuteAction(claim.ActionInstall, args)
}
27 changes: 27 additions & 0 deletions pkg/cnab/provider/install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cnabprovider

import (
"testing"

"github.com/cnabio/cnab-go/claim"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestRuntime_Install(t *testing.T) {
r := NewTestRuntime(t)
r.TestConfig.TestContext.AddTestFile("testdata/bundle.json", "bundle.json")

args := ActionArguments{
Instance: "mybuns",
BundlePath: "bundle.json",
}
err := r.Install(args)
require.NoError(t, err, "Install failed")

c, err := r.claims.ReadLastClaim(args.Instance)
require.NoError(t, err, "ReadLastClaim failed")

assert.Equal(t, claim.ActionInstall, c.Action, "wrong action recorded")
assert.Equal(t, args.Instance, c.Installation, "wrong installation name recorded")
}
105 changes: 1 addition & 104 deletions pkg/cnab/provider/invoke.go
Original file line number Diff line number Diff line change
@@ -1,108 +1,5 @@
package cnabprovider

import (
cnabaction "github.com/cnabio/cnab-go/action"
"github.com/cnabio/cnab-go/bundle"
"github.com/cnabio/cnab-go/claim"
"github.com/pkg/errors"
)

// getClaimForInvoke reads an instance from the runtime's claim storage. If one is not found, the bundle
// is examined to see if the action is stateless. If the action is stateless, we create a new, temporary, claim
// Returns a pointer to the claim, a flag to indicate if the claim is temporary, and an error if present.
func (r *Runtime) getClaimForInvoke(bun bundle.Bundle, actionName, instanceName string) (claim.Claim, bool, error) {
c, err := r.claims.ReadLastClaim(instanceName)
if err != nil {
if action, ok := bun.Actions[actionName]; ok {
if action.Stateless {
c = claim.Claim{
Action: actionName,
Installation: instanceName,
Bundle: bun,
}
return c, true, nil
}
}
return claim.Claim{}, false, errors.Wrapf(err, "could not load bundle instance %s", instanceName)
}
return c, false, nil
}

func (r *Runtime) Invoke(action string, args ActionArguments) error {
var b bundle.Bundle
var err error

if args.BundlePath != "" {
b, err = r.ProcessBundle(args.BundlePath)
if err != nil {
return err
}
}

existingClaim, isTempClaim, err := r.getClaimForInvoke(b, action, args.Instance)
if err != nil {
return err
}

// If the user didn't override the bundle definition, use the one
// from the existing claim
if args.BundlePath == "" {
b = existingClaim.Bundle
}

params, err := r.loadParameters(b, args.Params, args.ParameterSets, claim.ActionUpgrade)
if err != nil {
return errors.Wrap(err, "invalid parameters")
}

// TODO: (carolynvs) this should be consolidated with get claim for invoke, or teh logic in that function simplified
c, err := existingClaim.NewClaim(action, b, params)
if err != nil {
return err
}

creds, err := r.loadCredentials(c.Bundle, args.CredentialIdentifiers)
if err != nil {
return errors.Wrap(err, "could not load credentials")
}

driver, err := r.newDriver(args.Driver, args.Instance, args)
if err != nil {
return errors.Wrap(err, "unable to instantiate driver")
}

a := cnabaction.New(driver, r.claims)
a.SaveAllOutputs = true

modifies, err := c.IsModifyingAction()
if err != nil {
return err
}

// Only record runs that modify the bundle, e.g. don't save "logs" or "dry-run"
// In theory a custom action shouldn't ever have modifies AND stateless
// (which creates a temp claim) but just in case, if it does modify, we must
// persist.
shouldPersistClaim := modifies || !isTempClaim

if shouldPersistClaim {
err = a.SaveInitialClaim(c, claim.StatusRunning)
if err != nil {
return err
}
}

r.printDebugInfo(creds, params)

opResult, result, err := a.Run(c, creds, r.ApplyConfig(args)...)

if shouldPersistClaim {
if err != nil {
err = r.appendFailedResult(err, c)
return errors.Wrap(err, "failed to invoke the bundle")
}
return a.SaveOperationResult(opResult, c, result)
} else {
return errors.Wrap(err, "failed to invoke the bundle")
}
return r.ExecuteAction(action, args)
}
38 changes: 26 additions & 12 deletions pkg/cnab/provider/invoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (
"github.com/stretchr/testify/require"
)

func Test_Runtime_GetClaimForInvoke(t *testing.T) {
func TestRuntime_ClaimPersistence(t *testing.T) {
type input struct {
bun bundle.Bundle
action string
claim string
bun bundle.Bundle
action string
installation string
}

type result struct {
Expand All @@ -29,7 +29,10 @@ func Test_Runtime_GetClaimForInvoke(t *testing.T) {
want result
}

bunV, _ := bundle.GetDefaultSchemaVersion()
bun := bundle.Bundle{
SchemaVersion: bunV,
InvocationImages: []bundle.InvocationImage{{BaseImage: bundle.BaseImage{Image: "example.com/foo:v1.0.0"}}},
Actions: map[string]bundle.Action{
"blah": {
Stateless: true,
Expand Down Expand Up @@ -110,18 +113,29 @@ func Test_Runtime_GetClaimForInvoke(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
in := tc.in
want := tc.want
c, temp, err := d.getClaimForInvoke(in.bun, in.action, in.claim)

f, err := d.FileSystem.Create("bundle.json")
require.NoError(t, err, "could not open bundle.json")
_, err = tc.in.bun.WriteTo(f)
require.NoError(t, err, "could not write to bundle.json")

args := ActionArguments{
Instance: in.installation,
BundlePath: "bundle.json",
}
runErr := d.Invoke(in.action, args)
c, claimErr := d.claims.ReadLastClaim(in.installation)

if want.err == nil {
require.NoErrorf(t, err, "getClaimForInvoke failed")
assert.Equalf(t, want.temp, temp, "getClaimForInvoke returned an unexpected temporary flag")
assert.Equal(t, in.action, c.Action, "getClaimForInvoke returned a claim with an unexpected Action")
if temp {
assert.NotEqual(t, eClaim.ID, c.ID, "the temporary claim should have a new id")
require.NoErrorf(t, runErr, "Invoke failed")
if want.temp {
require.Error(t, claimErr, "temp claim should not be persisted")
} else {
assert.Equal(t, eClaim.ID, c.ID, "the claim should be a persisted claim")
require.NoError(t, claimErr, "the claim could not be read back")
assert.Equal(t, in.action, c.Action, "claim saved with wrong action")
}
} else {
require.EqualErrorf(t, err, want.err.Error(), "getClaimForInvoke returned an unexpected error")
require.EqualErrorf(t, runErr, want.err.Error(), "Invoke returned an unexpected error")
}
})
}
Expand Down
Loading

0 comments on commit 487403a

Please sign in to comment.