Skip to content

Commit

Permalink
refactor plugin interface to take PluginRequest rather than obj and a…
Browse files Browse the repository at this point in the history
…rgs (#76)

Refactor plugin interface to take PluginRequest rather than separate obj and args.
This allows for sending raw object json to a binary plugin for testing without
an expected separate line of entry for optional args.
  • Loading branch information
sseago authored Nov 1, 2021
1 parent 186bacf commit 72b0752
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 109 deletions.
30 changes: 18 additions & 12 deletions transform/binary-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,36 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/cli"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func main() {
u, err := cli.Unstructured(cli.ObjectReaderOrDie())
if err != nil {
cli.WriterErrorAndExit(fmt.Errorf("error getting unstructured object: %#v", err))
fields := []transform.OptionalFields{
{
FlagName: "MyFlag",
Help: "What the flag does",
Example: "true",
},
}
cli.RunAndExit(cli.NewCustomPlugin("OpenshiftCustomPlugin", Run), u)
cli.RunAndExit(cli.NewCustomPlugin("MyCustomPlugin", "v1", fields, Run))
}
func Run(u *unstructured.Unstructured) (transform.PluginResponse, error) {
func Run(request transform.PluginRequest) (transform.PluginResponse, error) {
// plugin writers need to write custome code here.
resp := transform.PluginResponse{}
// prepare the response
return resp, nil
resp := transform.PluginResponse{}
// prepare the response
return resp, nil
}
```

All it does is read an input from stdin, calls the Run function with the
input object passed as unstructured and prints the return value of `Run`
function on stdout.
function on stdout.

The json passed in via stdin is a `transform.PluginRequest` which consists of an
inline unstructured object and an optional `Extras` map containing additional
flags. Without any `Extras` the format is identical to the json output from
a `kubectl get -o json` call. When adding extra params, a map field "extras"
is added at the top level (parallel to "apiVersion", "kind", etc.).

During the development of the plugin, one can iterate by passing in the JSON
object on stdin manually. For example, if the above code is compiled and
Expand Down Expand Up @@ -120,7 +127,6 @@ object on stdin manually. For example, if the above code is compiled and
]
}
}
{}
```

Note: the json object was entered from console, and the response was `{}`
Expand Down
21 changes: 6 additions & 15 deletions transform/binary-plugin/binary_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/konveyor/crane-lib/transform"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

const (
Expand Down Expand Up @@ -65,10 +64,10 @@ func contain(validVersion transform.Version, versions []transform.Version) bool
return false
}

func (b *BinaryPlugin) Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func (b *BinaryPlugin) Run(request transform.PluginRequest) (transform.PluginResponse, error) {
p := transform.PluginResponse{}

out, errBytes, err := b.commandRunner.Run(u, extras, b.log)
out, errBytes, err := b.commandRunner.Run(request, b.log)
if err != nil {
b.log.Errorf("error running the plugin command")
return p, fmt.Errorf("error running the plugin command: %v", err)
Expand All @@ -95,7 +94,7 @@ func (b *BinaryPlugin) Metadata() transform.PluginMetadata {
}

type commandRunner interface {
Run(u *unstructured.Unstructured, extras map[string]string, log logrus.FieldLogger) ([]byte, []byte, error)
Run(request transform.PluginRequest, log logrus.FieldLogger) ([]byte, []byte, error)
Metadata(log logrus.FieldLogger) ([]byte, []byte, error)
}

Expand Down Expand Up @@ -136,19 +135,11 @@ func (b *binaryRunner) Metadata(log logrus.FieldLogger) ([]byte, []byte, error)

}

func (b *binaryRunner) Run(u *unstructured.Unstructured, extras map[string]string, log logrus.FieldLogger) ([]byte, []byte, error) {
objJson, err := u.MarshalJSON()
func (b *binaryRunner) Run(request transform.PluginRequest, log logrus.FieldLogger) ([]byte, []byte, error) {
objJson, err := request.MarshalJSON()
if err != nil {
log.Errorf("unable to marshal unstructured Object")
return nil, nil, fmt.Errorf("unable to marshal unstructured Object: %s, err: %v", u, err)
}
if len(extras) > 0 {
extrasJson, err := json.Marshal(extras)
if err != nil {
log.Errorf("unable to marshal extras map")
return nil, nil, fmt.Errorf("unable to marshal extras map: %v, err: %v", extras, err)
}
objJson = append(objJson, extrasJson...)
return nil, nil, fmt.Errorf("unable to marshal unstructured Object: %s, err: %v", request, err)
}

command := cliContext.getCommand(b.pluginPath)
Expand Down
4 changes: 2 additions & 2 deletions transform/binary-plugin/binary_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type fakeCommandRunner struct {
metadataStdout, metadataStderr []byte
}

func (f *fakeCommandRunner) Run(_ *unstructured.Unstructured, _ map[string]string, _ logrus.FieldLogger) ([]byte, []byte, error) {
func (f *fakeCommandRunner) Run(_ transform.PluginRequest, _ logrus.FieldLogger) ([]byte, []byte, error) {
return f.stdout, f.stderr, f.errorRunningCommand
}

Expand Down Expand Up @@ -342,7 +342,7 @@ func TestBinaryPlugin_Run(t *testing.T) {
},
log: logrus.New().WithField("test", tt.name),
}
got, err := b.Run(&unstructured.Unstructured{}, nil)
got, err := b.Run(transform.PluginRequest{Unstructured:unstructured.Unstructured{}})
if (err != nil) != tt.wantErr {
t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
14 changes: 7 additions & 7 deletions transform/binary-plugin/examples/openshift/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/cli"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

var logger logrus.FieldLogger
Expand Down Expand Up @@ -59,10 +58,11 @@ func getOptionalFields(extras map[string]string) (openshiftOptionalFields, error
return fields, nil
}

func Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func Run(request transform.PluginRequest) (transform.PluginResponse, error) {
u := request.Unstructured
var patch jsonpatch.Patch
whiteOut := false
inputFields, err := getOptionalFields(extras)
inputFields, err := getOptionalFields(request.Extras)
if err != nil {
return transform.PluginResponse{}, err
}
Expand All @@ -73,16 +73,16 @@ func Run(u *unstructured.Unstructured, extras map[string]string) (transform.Plug
whiteOut = true
case "BuildConfig":
logger.Info("found build config, processing")
patch, err = UpdateBuildConfig(*u, inputFields)
patch, err = UpdateBuildConfig(u, inputFields)
case "Pod":
logger.Info("found pod, processing update default pull secret")
patch, err = UpdateDefaultPullSecrets(*u, inputFields)
patch, err = UpdateDefaultPullSecrets(u, inputFields)
case "Route":
logger.Info("found route, processing")
patch, err = UpdateRoute(*u)
patch, err = UpdateRoute(u)
case "ServiceAccount":
logger.Info("found service account, processing")
patch, err = UpdateServiceAccount(*u)
patch, err = UpdateServiceAccount(u)
}
if err != nil {
return transform.PluginResponse{}, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/cli"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func main() {
Expand All @@ -20,9 +19,9 @@ func main() {
cli.RunAndExit(cli.NewCustomPlugin("AnnotationPlugin", "v1", fields, Run))
}

func Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func Run(request transform.PluginRequest) (transform.PluginResponse, error) {
// plugin writers need to write custome code here.
patch, err := AddAnnotation(*u, extras)
patch, err := AddAnnotation(request)

if err != nil {
return transform.PluginResponse{}, err
Expand All @@ -34,8 +33,8 @@ func Run(u *unstructured.Unstructured, extras map[string]string) (transform.Plug
}, nil
}

func AddAnnotation(u unstructured.Unstructured, extras map[string]string) (jsonpatch.Patch, error) {
val, ok := extras["annotation-value"]
func AddAnnotation(request transform.PluginRequest) (jsonpatch.Patch, error) {
val, ok := request.Extras["annotation-value"]
if !ok {
val = "crane"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/cli"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func main() {
cli.RunAndExit(cli.NewCustomPlugin("WhiteoutPluginAll", "v1", nil, Run))
}

func Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func Run(request transform.PluginRequest) (transform.PluginResponse, error) {
// plugin writers need to write custome code here.
var patch jsonpatch.Patch
return transform.PluginResponse{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/cli"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func main() {
cli.RunAndExit(cli.NewCustomPlugin("WhiteoutPodsPlugin", "v1", nil, Run))
}

func Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func Run(request transform.PluginRequest) (transform.PluginResponse, error) {
// plugin writers need to write custome code here.
var patch jsonpatch.Patch
var whiteout bool
if u.GetKind() == "Pod" {
if request.Unstructured.GetKind() == "Pod" {
whiteout = true
}
return transform.PluginResponse{
Expand Down
55 changes: 25 additions & 30 deletions transform/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cli
import (
"bytes"
"encoding/json"
goerrors "errors"
"fmt"
"io"
"os"
Expand All @@ -12,7 +11,6 @@ import (

"github.com/konveyor/crane-lib/transform"
"github.com/konveyor/crane-lib/transform/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

var (
Expand All @@ -36,21 +34,21 @@ func init() {
type customPlugin struct {
// TODO: figure out a way to include the name of the plugin in the error messages.
metadata transform.PluginMetadata
runFunc func(*unstructured.Unstructured, map[string]string) (transform.PluginResponse, error)
runFunc func(transform.PluginRequest) (transform.PluginResponse, error)
}

func (c *customPlugin) Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
func (c *customPlugin) Run(request transform.PluginRequest) (transform.PluginResponse, error) {
if c.runFunc == nil {
return transform.PluginResponse{}, nil
}
return c.runFunc(u, extras)
return c.runFunc(request)
}

func (c *customPlugin) Metadata() transform.PluginMetadata {
return c.metadata
}

func NewCustomPlugin(name, version string, optionalFields []transform.OptionalFields, runFunc func(*unstructured.Unstructured, map[string]string) (transform.PluginResponse, error)) transform.Plugin {
func NewCustomPlugin(name, version string, optionalFields []transform.OptionalFields, runFunc func(transform.PluginRequest) (transform.PluginResponse, error)) transform.Plugin {
return &customPlugin{
metadata: transform.PluginMetadata{
Name: name,
Expand Down Expand Up @@ -102,41 +100,38 @@ func RunAndExit(plugin transform.Plugin) {

// Ignoring this error as anthing wrong here will be caught in the unmarshalJSON below
b, _ := json.Marshal(m)
u := unstructured.Unstructured{}
err = u.UnmarshalJSON(b)
req := transform.PluginRequest{}
err = json.Unmarshal(b, &req)
if err != nil {
WriterErrorAndExit(&errors.PluginError{
Type: errors.PluginInvalidInputError,
Message: "error writing plugin response to stdOut",
ErrorMessage: err.Error(),
})
}

extrasIn := map[string]interface{}{}

err = decoder.Decode(&extrasIn)
if err != nil && !goerrors.Is(err, io.EOF) {
WriterErrorAndExit(&errors.PluginError{
Type: errors.PluginInvalidIOError,
Message: "error reading extras input",
ErrorMessage: err.Error(),
})
extrasIn, ok := m["extras"]
var extrasInMap map[string]interface{}
if ok {
extrasInMap, ok = extrasIn.(map[string]interface{})
}
extras := map[string]string{}
for key, value := range extrasIn {
switch value.(type) {
case string:
extras[key] = value.(string)
default:
WriterErrorAndExit(&errors.PluginError{
Type: errors.PluginInvalidIOError,
Message: "error getting extras value string",
ErrorMessage: fmt.Sprintf("value %v for param %v is not a string", value, key),
})
if ok {
extras := map[string]string{}
for key, value := range extrasInMap {
switch value.(type) {
case string:
extras[key] = value.(string)
default:
WriterErrorAndExit(&errors.PluginError{
Type: errors.PluginInvalidIOError,
Message: "error getting extras value string",
ErrorMessage: fmt.Sprintf("value %v for param %v is not a string", value, key),
})
}
}
req.Extras = extras
}

resp, err := plugin.Run(&u, extras)
resp, err := plugin.Run(req)
if err != nil {
WriterErrorAndExit(&errors.PluginError{
Type: errors.PluginRunError,
Expand Down
6 changes: 3 additions & 3 deletions transform/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestRunAndExit(t *testing.T) {
metadata *transform.PluginMetadata
wantErr bool
wantedErr errors.PluginError
fakeFunc func(*unstructured.Unstructured, map[string]string) (transform.PluginResponse, error)
fakeFunc func(transform.PluginRequest) (transform.PluginResponse, error)
version string
errCapture bytes.Buffer
outCapture bytes.Buffer
Expand All @@ -90,7 +90,7 @@ func TestRunAndExit(t *testing.T) {
IsWhiteOut: false,
Patches: []jsonpatch.Operation{},
},
fakeFunc: func(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
fakeFunc: func(request transform.PluginRequest) (transform.PluginResponse, error) {
return transform.PluginResponse{
Version: "v1",
IsWhiteOut: false,
Expand Down Expand Up @@ -152,7 +152,7 @@ func TestRunAndExit(t *testing.T) {
}},
err: nil,
},
fakeFunc: func(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
fakeFunc: func(request transform.PluginRequest) (transform.PluginResponse, error) {
return transform.PluginResponse{}, fmt.Errorf("invalid run")
},
errCapture: bytes.Buffer{},
Expand Down
8 changes: 4 additions & 4 deletions transform/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,17 @@ func (k KubernetesTransformPlugin) setOptionalFields(extras map[string]string) {
}
}

func (k KubernetesTransformPlugin) Run(u *unstructured.Unstructured, extras map[string]string) (transform.PluginResponse, error) {
k.setOptionalFields(extras)
func (k KubernetesTransformPlugin) Run(request transform.PluginRequest) (transform.PluginResponse, error) {
k.setOptionalFields(request.Extras)
resp := transform.PluginResponse{}
// Set version in the future
resp.Version = string(transform.V1)
var err error
resp.IsWhiteOut = k.getWhiteOuts(*u)
resp.IsWhiteOut = k.getWhiteOuts(request.Unstructured)
if resp.IsWhiteOut {
return resp, err
}
resp.Patches, err = k.getKubernetesTransforms(*u)
resp.Patches, err = k.getKubernetesTransforms(request.Unstructured)
return resp, err

}
Expand Down
Loading

0 comments on commit 72b0752

Please sign in to comment.