Implement Workload Run API

implemented workload run api and write e2e test
cases for workload run cli and API
This commit is contained in:
zzxwill
2020-08-19 18:16:09 +08:00
parent ae17dd3ffa
commit 657cb91db3
11 changed files with 264 additions and 83 deletions

View File

@@ -3,15 +3,13 @@ package cmd
import (
"context"
"errors"
"fmt"
"strconv"
"github.com/cloud-native-application/rudrx/pkg/oam"
"github.com/cloud-native-application/rudrx/api/types"
"github.com/cloud-native-application/rudrx/pkg/application"
"github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/cloud-native-application/rudrx/pkg/plugins"
"cuelang.org/go/cue"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -19,16 +17,7 @@ import (
const Staging = "staging"
const App = "app"
type runOptions struct {
Template types.Capability
Env *types.EnvMeta
workloadName string
client client.Client
app *application.Application
appName string
staging bool
util.IOStreams
}
type runOptions oam.RunOptions
func newRunOptions(ioStreams util.IOStreams) *runOptions {
return &runOptions{IOStreams: ioStreams}
@@ -53,11 +42,12 @@ func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams util.
Example: "vela " + name + ":run frontend",
RunE: func(cmd *cobra.Command, args []string) error {
o := newRunOptions(ioStreams)
o.WorkloadType = name
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.client = newClient
o.KubeClient = newClient
o.Env, err = GetEnv(cmd)
if err != nil {
return err
@@ -89,48 +79,15 @@ func (o *runOptions) Complete(cmd *cobra.Command, args []string, ctx context.Con
if argsLength < 1 {
return errors.New("must specify name for workload")
}
o.workloadName = args[0]
if app := cmd.Flag(App).Value.String(); app != "" {
o.appName = app
} else {
o.appName = o.workloadName
}
app, err := application.Load(o.Env.Name, o.appName)
if err != nil {
return err
}
app.Name = o.appName
workloadName := args[0]
template := o.Template
appGroup := cmd.Flag(App).Value.String()
if app.Components == nil {
app.Components = make(map[string]map[string]interface{})
}
tp, workloadData := app.GetWorkload(o.workloadName)
if tp == "" {
// Not exist
tp = o.Template.Name
}
for _, v := range o.Template.Parameters {
flagSet := cmd.Flag(v.Name)
switch v.Type {
case cue.IntKind:
d, _ := strconv.ParseInt(flagSet.Value.String(), 10, 64)
workloadData[v.Name] = d
case cue.StringKind:
workloadData[v.Name] = flagSet.Value.String()
case cue.BoolKind:
d, _ := strconv.ParseBool(flagSet.Value.String())
workloadData[v.Name] = d
case cue.NumberKind, cue.FloatKind:
d, _ := strconv.ParseFloat(flagSet.Value.String(), 64)
workloadData[v.Name] = d
}
}
if err = app.SetWorkload(o.workloadName, tp, workloadData); err != nil {
return err
}
o.app = app
return app.Save(o.Env.Name, o.appName)
envName := o.Env.Name
var flagSet = cmd.Flags()
app, err := oam.BaseComplete(envName, workloadName, appGroup, flagSet, template)
o.App = app
return err
}
func (o *runOptions) Run(cmd *cobra.Command) error {
@@ -138,14 +95,10 @@ func (o *runOptions) Run(cmd *cobra.Command) error {
if err != nil {
return err
}
if staging {
o.Info("Staging saved")
return nil
msg, err := oam.BaseRun(staging, o.App, o.KubeClient, o.Env)
if err != nil {
return err
}
o.Infof("Creating App %s\n", o.app.Name)
if err := o.app.Run(context.Background(), o.client, o.Env); err != nil {
return fmt.Errorf("create app err: %s", err)
}
o.Info("SUCCEED")
o.Info(msg)
return nil
}

View File

@@ -3,7 +3,6 @@ package oam
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -74,7 +73,7 @@ func ListEnvs(envName string) ([]*types.EnvMeta, error) {
env, err := GetEnvByName(envName)
if err != nil {
if os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("env %s not exist", envName))
err = fmt.Errorf("env %s not exist", envName)
}
return envList, err
}

View File

@@ -3,7 +3,6 @@ package oam
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -80,5 +79,5 @@ func GetTraitDefinitionByKind(ctx context.Context, c client.Client, traitKind st
return t, nil
}
}
return traitDefinition, errors.New(fmt.Sprintf("Could not find TraitDefinition by kind %s", traitKind))
return traitDefinition, fmt.Errorf("could not find TraitDefinition by kind %s", traitKind)
}

90
pkg/oam/workload.go Normal file
View File

@@ -0,0 +1,90 @@
package oam
import (
"context"
"fmt"
"strconv"
"github.com/spf13/pflag"
"github.com/cloud-native-application/rudrx/api/types"
"github.com/cloud-native-application/rudrx/pkg/cmd/util"
"sigs.k8s.io/controller-runtime/pkg/client"
"cuelang.org/go/cue"
"github.com/cloud-native-application/rudrx/pkg/application"
)
type RunOptions struct {
Template types.Capability
Env *types.EnvMeta
WorkloadType string
WorkloadName string
KubeClient client.Client
App *application.Application
AppName string
Staging bool
util.IOStreams
}
func BaseComplete(envName string, workloadName string, appGroup string, flagSet *pflag.FlagSet, template types.Capability) (*application.Application, error) {
var appName string
if appGroup != "" {
appName = appGroup
} else {
appName = workloadName
}
app, err := application.Load(envName, appName)
if err != nil {
return app, err
}
app.Name = appName
if app.Components == nil {
app.Components = make(map[string]map[string]interface{})
}
tp, workloadData := app.GetWorkload(workloadName)
if tp == "" {
// Not exist
tp = template.Name
}
for _, v := range template.Parameters {
flagValue, _ := flagSet.GetString(v.Name)
// Cli can check required flag before make a request to backend, but API itself could not, so validate flags here
if v.Required && v.Name != "name" && flagValue == "" {
return app, fmt.Errorf("required flag(s) \"%s\" not set", v.Name)
}
switch v.Type {
case cue.IntKind:
d, _ := strconv.ParseInt(flagValue, 10, 64)
workloadData[v.Name] = d
case cue.StringKind:
workloadData[v.Name] = flagValue
case cue.BoolKind:
d, _ := strconv.ParseBool(flagValue)
workloadData[v.Name] = d
case cue.NumberKind, cue.FloatKind:
d, _ := strconv.ParseFloat(flagValue, 64)
workloadData[v.Name] = d
}
}
if err = app.SetWorkload(workloadName, tp, workloadData); err != nil {
return app, err
}
return app, app.Save(envName, appName)
}
func BaseRun(staging bool, App *application.Application, kubeClient client.Client, Env *types.EnvMeta) (string, error) {
if staging {
return "Staging saved", nil
}
var msg string
msg = fmt.Sprintf("Creating App %s\n", App.Name)
if err := App.Run(context.Background(), kubeClient, Env); err != nil {
err = fmt.Errorf("create app err: %s", err)
return "", err
}
msg += "SUCCEED"
return msg, nil
}

View File

@@ -1,6 +1,8 @@
package apis
import "k8s.io/apimachinery/pkg/runtime"
import (
"k8s.io/apimachinery/pkg/runtime"
)
type Environment struct {
EnvironmentName string `json:"environmentName" binding:"required,min=1,max=32"`
@@ -18,3 +20,15 @@ type Response struct {
Code int `json:"code"`
Data interface{} `json:"data"`
}
type WorkloadFlag struct {
Name string `json:"name"`
Value string `json:"value"`
}
type WorkloadRunBody struct {
EnvName string `json:"env_name"`
WorkloadType string `json:"workload_type"`
WorkloadName string `json:"workload_name"`
AppGroup string `json:"app_group"`
Flags []WorkloadFlag `json:"flags"`
Staging bool `json:"staging"`
}

View File

@@ -1,9 +1,49 @@
package handler
import "github.com/gin-gonic/gin"
import (
"github.com/cloud-native-application/rudrx/api/types"
"github.com/cloud-native-application/rudrx/pkg/oam"
"github.com/cloud-native-application/rudrx/pkg/plugins"
"github.com/spf13/pflag"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/cloud-native-application/rudrx/pkg/server/apis"
"github.com/cloud-native-application/rudrx/pkg/server/util"
"github.com/gin-gonic/gin"
)
// Workload related handlers
func CreateWorkload(c *gin.Context) {
kubeClient := c.MustGet("KubeClient")
var body apis.WorkloadRunBody
if err := c.ShouldBindJSON(&body); err != nil {
util.HandleError(c, util.InvalidArgument, "the workload run request body is invalid")
return
}
fs := pflag.NewFlagSet("workload", pflag.ContinueOnError)
for _, f := range body.Flags {
fs.String(f.Name, f.Value, "")
}
evnName := body.EnvName
var template types.Capability
template, err := plugins.LoadCapabilityByName(body.WorkloadType)
appObj, err := oam.BaseComplete(evnName, body.WorkloadName, body.AppGroup, fs, template)
if err != nil {
util.HandleError(c, util.StatusInternalServerError, err.Error())
return
}
env, err := oam.GetEnvByName(evnName)
if err != nil {
util.HandleError(c, util.StatusInternalServerError, err.Error())
return
}
msg, err := oam.BaseRun(body.Staging, appObj, kubeClient.(client.Client), env)
if err != nil {
util.HandleError(c, util.StatusInternalServerError, err.Error())
return
}
util.AssembleResponse(c, msg, err)
}
func UpdateWorkload(c *gin.Context) {