package cmd
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"path"
"path/filepath"
"strings"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/vmware/dispatch/pkg/api/v1"
"github.com/vmware/dispatch/pkg/dispatchcli/i18n"
"github.com/vmware/dispatch/pkg/utils"
)
var (
createLong = i18n.T(`Create a resource. See subcommands for resources that can be created.`)
createExample = i18n.T(``)
file = i18n.T(``)
workDir = i18n.T(``)
)
type ModelAction func(interface{}) error
type importFunction struct {
v1.Function
}
func resolveFileReference(ref string) (string, error) {
if strings.HasPrefix(ref, "@") {
filePath := path.Join(workDir, (ref)[1:])
fileContent, err := ioutil.ReadFile(filePath)
if err != nil {
return "", errors.Wrapf(err, "Error when reading content of %s", filePath)
}
encoded := string(fileContent)
return encoded, nil
}
return ref, nil
}
func importFile(out io.Writer, errOut io.Writer, cmd *cobra.Command, args []string, actionMap map[string]ModelAction, actionName string) error {
fullPath := path.Join(workDir, file)
b, err := ioutil.ReadFile(fullPath)
if err != nil {
return errors.Wrapf(err, "Error reading file %s", fullPath)
}
return importBytes(out, b, actionMap, actionName)
}
func importBytes(out io.Writer, b []byte, actionMap map[string]ModelAction, actionName string) error {
var err error
docs := bytes.Split(b, []byte("---"))
type kind struct {
Kind string `json:"kind"`
}
type output struct {
Endpoints []*v1.Endpoint `json:"endpoint"`
BaseImages []*v1.BaseImage `json:"baseImages"`
Images []*v1.Image `json:"images"`
DriverTypes []*v1.EventDriverType `json:"driverTypes"`
Drivers []*v1.EventDriver `json:"drivers"`
Subscriptions []*v1.Subscription `json:"subscriptions"`
Functions []*v1.Function `json:"functions"`
Secrets []*v1.Secret `json:"secrets"`
Policies []*v1.Policy `json:"policies"`
ServiceInstances []*v1.ServiceInstance `json:"serviceInstances"`
ServiceAccounts []*v1.ServiceAccount `json:"serviceaccounts"`
Organizations []*v1.Organization `json:"organizations"`
}
o := output{}
for _, doc := range docs {
k := &kind{}
err = yaml.Unmarshal(doc, k)
if err != nil {
return errors.Wrapf(err, "Error decoding document %s", doc)
}
switch docKind := k.Kind; docKind {
case v1.EndpointKind:
m := &v1.Endpoint{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding api document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Endpoints = append(o.Endpoints, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, m.Name)
case v1.BaseImageKind:
m := &v1.BaseImage{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding base image document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.BaseImages = append(o.BaseImages, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, m.Name)
case v1.ImageKind:
m := &v1.Image{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding image document %s", doc)
}
if m.RuntimeDependencies != nil {
manifest, err := resolveFileReference(m.RuntimeDependencies.Manifest)
if err != nil {
return err
}
m.RuntimeDependencies.Manifest = manifest
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Images = append(o.Images, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, m.Name)
case v1.FunctionKind:
m := &v1.Function{}
if err := yaml.Unmarshal(doc, m); err != nil {
return errors.Wrapf(err, "Error decoding function document %s", doc)
}
if m.SourcePath != "" {
sourcePath := filepath.Join(workDir, m.SourcePath)
isDir, err := utils.IsDir(sourcePath)
if err != nil {
return err
}
if isDir && m.Handler == "" {
return fmt.Errorf("error creating function %s: handler is required, source path %s is a directory", m.Name, sourcePath)
}
sourceTarGz, err := utils.TarGzBytes(sourcePath)
if err != nil {
return errors.Wrapf(err, "Error when reading content of %s", sourcePath)
}
m.Source = sourceTarGz
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Functions = append(o.Functions, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, m.Name)
case v1.DriverTypeKind:
m := &v1.EventDriverType{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error when decoding driver type document of %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.DriverTypes = append(o.DriverTypes, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.DriverKind:
m := &v1.EventDriver{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding driver document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Drivers = append(o.Drivers, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.SubscriptionKind:
m := &v1.Subscription{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding subscription document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Subscriptions = append(o.Subscriptions, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.SecretKind:
m := &v1.Secret{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding secret document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Secrets = append(o.Secrets, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.PolicyKind:
m := &v1.Policy{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding policy document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Policies = append(o.Policies, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.ServiceAccountKind:
m := &v1.ServiceAccount{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding service account document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.ServiceAccounts = append(o.ServiceAccounts, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
case v1.OrganizationKind:
m := &v1.Organization{}
err = yaml.Unmarshal(doc, m)
if err != nil {
return errors.Wrapf(err, "Error decoding organization document %s", doc)
}
err = actionMap[docKind](m)
if err != nil {
return err
}
o.Organizations = append(o.Organizations, m)
fmt.Fprintf(out, "%s %s: %s\n", actionName, docKind, *m.Name)
default:
continue
}
}
return nil
}
var createMap map[string]ModelAction
func initCreateMap() {
fnClient := functionsClient()
imgClient := imagesClient()
baseImgClient := baseImagesClient()
eventClient := eventManagerClient()
endpointClient := endpointsClient()
secClient := secretsClient()
iamClient := identityManagerClient()
createMap = map[string]ModelAction{
v1.ImageKind: CallCreateImage(imgClient),
v1.BaseImageKind: CallCreateBaseImage(baseImgClient),
v1.FunctionKind: CallCreateFunction(fnClient),
v1.SecretKind: CallCreateSecret(secClient),
v1.PolicyKind: CallCreatePolicy(iamClient),
v1.ServiceAccountKind: CallCreateServiceAccount(iamClient),
v1.DriverTypeKind: CallCreateEventDriverType(eventClient),
v1.DriverKind: CallCreateEventDriver(eventClient),
v1.SubscriptionKind: CallCreateSubscription(eventClient),
v1.EndpointKind: CallCreateEndpoint(endpointClient),
v1.OrganizationKind: callCreateOrganization(iamClient),
}
}
func NewCmdCreate(out io.Writer, errOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: i18n.T("Create resources."),
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
if file == "" {
runHelp(cmd, args)
return
}
initCreateMap()
err := importFile(out, errOut, cmd, args, createMap, "Created")
CheckErr(err)
},
}
cmd.Flags().StringVarP(&file, "file", "f", "", "Path to YAML file")
cmd.Flags().StringVarP(&workDir, "work-dir", "w", "", "Working directory relative paths are based on")
cmd.AddCommand(NewCmdCreateBaseImage(out, errOut))
cmd.AddCommand(NewCmdCreateImage(out, errOut))
cmd.AddCommand(NewCmdCreateFunction(out, errOut))
cmd.AddCommand(NewCmdCreateSecret(out, errOut))
cmd.AddCommand(NewCmdCreateAPI(out, errOut))
cmd.AddCommand(NewCmdCreateSubscription(out, errOut))
cmd.AddCommand(NewCmdCreateEventDriver(out, errOut))
cmd.AddCommand(NewCmdCreateEventDriverType(out, errOut))
cmd.AddCommand(NewCmdCreateSeedImages(out, errOut))
return cmd
}