mirror of
https://github.com/kubevela/kubevela.git
synced 2026-05-06 01:17:09 +00:00
implement appfile oriented OAM Application run
This commit is contained in:
@@ -32,6 +32,11 @@ type Source struct {
|
||||
ChartName string `json:"chartName,omitempty"`
|
||||
}
|
||||
|
||||
type CrdInfo struct {
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
|
||||
// Capability defines the content of a capability
|
||||
type Capability struct {
|
||||
Name string `json:"name"`
|
||||
@@ -47,6 +52,7 @@ type Capability struct {
|
||||
// Plugin Source
|
||||
Source *Source `json:"source,omitempty"`
|
||||
Install *Installation `json:"install,omitempty"`
|
||||
CrdInfo *CrdInfo `json:"crdInfo,omitempty"`
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultOAMNS = "oam-system"
|
||||
DefaultOAMReleaseName = "core-runtime"
|
||||
@@ -14,116 +8,22 @@ const (
|
||||
DefaultOAMRepoUrl = "https://charts.crossplane.io/master"
|
||||
DefaultOAMVersion = ">0.0.0-0"
|
||||
|
||||
DefaultEnvName = "default"
|
||||
DefaultEnvName = "default"
|
||||
DefaultAppNamespace = "default"
|
||||
)
|
||||
|
||||
const (
|
||||
Traits = "traits"
|
||||
Scopes = "scopes"
|
||||
AnnApiVersion = "oam.appengine.info/apiVersion"
|
||||
AnnKind = "oam.appengine.info/kind"
|
||||
|
||||
// ComponentWorkloadDefLabel indicate which workloaddefinition generate from
|
||||
ComponentWorkloadDefLabel = "vela.oam.dev/workloadDef"
|
||||
TraitDefLabel = "vela.oam.dev/traitDef"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
// key of map is component name
|
||||
Components map[string]map[string]interface{} `json:"components"`
|
||||
Secrets map[string]map[string]interface{} `json:"secrets"`
|
||||
Scopes map[string]map[string]interface{} `json:"appScopes"`
|
||||
}
|
||||
|
||||
func (app *Application) Valid() error {
|
||||
if app.Name == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
if len(app.Components) == 0 {
|
||||
return errors.New("at least one component is required")
|
||||
}
|
||||
for name, comp := range app.Components {
|
||||
lenth := len(comp)
|
||||
if traits, ok := comp[Traits]; ok {
|
||||
lenth--
|
||||
trs, ok := traits.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("format of traits in %s must be map", name)
|
||||
}
|
||||
for traitName, tr := range trs {
|
||||
_, ok := tr.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("trait %s in %s must be map", traitName, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if scopes, ok := comp[Scopes]; ok {
|
||||
lenth--
|
||||
_, ok := scopes.([]string)
|
||||
if !ok {
|
||||
return fmt.Errorf("format of scopes in %s must be string array", name)
|
||||
}
|
||||
}
|
||||
if lenth != 1 {
|
||||
return fmt.Errorf("you must have only one workload in component %s", name)
|
||||
}
|
||||
for workloadType, workload := range comp {
|
||||
if NotWorkload(workloadType) {
|
||||
continue
|
||||
}
|
||||
_, ok := workload.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("format of workload in %s must be map", name)
|
||||
}
|
||||
//TODO(wonderflow) check workload type exists
|
||||
//TODO(wonderflow) check arguments of workload is valid
|
||||
}
|
||||
}
|
||||
//TODO(wonderflow) check scope types
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotWorkload(tp string) bool {
|
||||
if tp == Scopes || tp == Traits {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (app *Application) GetComponents() []string {
|
||||
var components []string
|
||||
for name := range app.Components {
|
||||
components = append(components, name)
|
||||
}
|
||||
sort.Strings(components)
|
||||
return components
|
||||
}
|
||||
|
||||
func (app *Application) GetWorkload(componentName string) (string, map[string]interface{}, error) {
|
||||
comp, ok := app.Components[componentName]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("%s not exist", componentName)
|
||||
}
|
||||
for tp, workload := range comp {
|
||||
if NotWorkload(tp) {
|
||||
continue
|
||||
}
|
||||
return tp, workload.(map[string]interface{}), nil
|
||||
}
|
||||
return "", nil, fmt.Errorf("workload not exist in %s", componentName)
|
||||
}
|
||||
|
||||
func (app *Application) GetTraits(componentName string) (map[string]interface{}, error) {
|
||||
comp, ok := app.Components[componentName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s not exist", componentName)
|
||||
}
|
||||
t, ok := comp[Traits]
|
||||
if !ok {
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
// assume it's valid, use Valid() to check
|
||||
traits := t.(map[string]interface{})
|
||||
return traits, nil
|
||||
}
|
||||
|
||||
type EnvMeta struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -1 +1 @@
|
||||
# RudrX Dashboard
|
||||
# Vela Dashboard
|
||||
|
||||
269
pkg/application/app.go
Normal file
269
pkg/application/app.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
mycue "github.com/cloud-native-application/rudrx/pkg/cue"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/pkg/plugins"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/pkg/utils/system"
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
Traits = "traits"
|
||||
Scopes = "scopes"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
// key of map is component name
|
||||
Components map[string]map[string]interface{} `json:"components"`
|
||||
Secrets map[string]map[string]interface{} `json:"secrets"`
|
||||
Scopes map[string]map[string]interface{} `json:"appScopes"`
|
||||
}
|
||||
|
||||
func Load(envName, appName string) (*Application, error) {
|
||||
appDir, err := system.GetApplicationDir(envName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get app dir from env %s err %v", envName, err)
|
||||
}
|
||||
app := &Application{Name: appName}
|
||||
data, err := ioutil.ReadFile(filepath.Join(appDir, appName+".yaml"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return app, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
err = yaml.Unmarshal(data, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (app *Application) Save(envName, appName string) error {
|
||||
appDir, err := system.GetApplicationDir(envName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get app dir from env %s err %v", envName, err)
|
||||
}
|
||||
out, err := yaml.Marshal(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filepath.Join(appDir, appName+".yaml"), out, 0644)
|
||||
}
|
||||
|
||||
func (app *Application) Validate() error {
|
||||
if app.Name == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
if len(app.Components) == 0 {
|
||||
return errors.New("at least one component is required")
|
||||
}
|
||||
for name, comp := range app.Components {
|
||||
lenth := len(comp)
|
||||
if traits, ok := comp[Traits]; ok {
|
||||
lenth--
|
||||
trs, ok := traits.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("format of traits in %s must be map", name)
|
||||
}
|
||||
for traitName, tr := range trs {
|
||||
_, ok := tr.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("trait %s in %s must be map", traitName, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if scopes, ok := comp[Scopes]; ok {
|
||||
lenth--
|
||||
_, ok := scopes.([]string)
|
||||
if !ok {
|
||||
return fmt.Errorf("format of scopes in %s must be string array", name)
|
||||
}
|
||||
}
|
||||
if lenth != 1 {
|
||||
return fmt.Errorf("you must have only one workload in component %s", name)
|
||||
}
|
||||
for workloadType, workload := range comp {
|
||||
if NotWorkload(workloadType) {
|
||||
continue
|
||||
}
|
||||
_, ok := workload.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("format of workload in %s must be map", name)
|
||||
}
|
||||
//TODO(wonderflow) check workload type exists
|
||||
//TODO(wonderflow) check arguments of workload is valid
|
||||
}
|
||||
}
|
||||
//TODO(wonderflow) check scope types
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotWorkload(tp string) bool {
|
||||
if tp == Scopes || tp == Traits {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (app *Application) GetComponents() []string {
|
||||
var components []string
|
||||
for name := range app.Components {
|
||||
components = append(components, name)
|
||||
}
|
||||
sort.Strings(components)
|
||||
return components
|
||||
}
|
||||
|
||||
func (app *Application) GetWorkload(componentName string) (string, map[string]interface{}, error) {
|
||||
comp, ok := app.Components[componentName]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("%s not exist", componentName)
|
||||
}
|
||||
for tp, workload := range comp {
|
||||
if NotWorkload(tp) {
|
||||
continue
|
||||
}
|
||||
return tp, workload.(map[string]interface{}), nil
|
||||
}
|
||||
return "", nil, fmt.Errorf("workload not exist in %s", componentName)
|
||||
}
|
||||
|
||||
func (app *Application) GetTraits(componentName string) (map[string]map[string]interface{}, error) {
|
||||
comp, ok := app.Components[componentName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s not exist", componentName)
|
||||
}
|
||||
t, ok := comp[Traits]
|
||||
if !ok {
|
||||
return make(map[string]map[string]interface{}), nil
|
||||
}
|
||||
// assume it's valid, use Validate() to check
|
||||
trs := t.(map[string]interface{})
|
||||
traits := make(map[string]map[string]interface{})
|
||||
for k, v := range trs {
|
||||
traits[k] = v.(map[string]interface{})
|
||||
}
|
||||
return traits, nil
|
||||
}
|
||||
|
||||
func (app *Application) GetTraitsByType(componentName, traitType string) (map[string]interface{}, error) {
|
||||
traits, err := app.GetTraits(componentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for t, tt := range traits {
|
||||
if t == traitType {
|
||||
return tt, nil
|
||||
}
|
||||
}
|
||||
return make(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
func (app *Application) GetWorkloadObject(componentName string) (*unstructured.Unstructured, error) {
|
||||
workloadType, workloadData, err := app.GetWorkload(componentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return EvalToObject(workloadType, workloadData)
|
||||
}
|
||||
|
||||
func EvalToObject(capName string, data map[string]interface{}) (*unstructured.Unstructured, error) {
|
||||
cap, err := plugins.LoadCapabilityByName(capName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsondata, err := mycue.Eval(cap.DefinitionPath, capName, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var obj = make(map[string]interface{})
|
||||
if err = json.Unmarshal([]byte(jsondata), &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := &unstructured.Unstructured{Object: obj}
|
||||
if cap.CrdInfo != nil {
|
||||
u.SetAPIVersion(cap.CrdInfo.ApiVersion)
|
||||
u.SetKind(cap.CrdInfo.Kind)
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (app *Application) GetComponentTraits(componentName string) ([]v1alpha2.ComponentTrait, error) {
|
||||
var traits []v1alpha2.ComponentTrait
|
||||
rawTraits, err := app.GetTraits(componentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for traitType, traitData := range rawTraits {
|
||||
obj, err := EvalToObject(traitType, traitData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO(wonderflow): handle trait data input/output here
|
||||
obj.SetAnnotations(map[string]string{types.TraitDefLabel: traitType})
|
||||
traits = append(traits, v1alpha2.ComponentTrait{Trait: runtime.RawExtension{Object: obj}})
|
||||
}
|
||||
return traits, nil
|
||||
}
|
||||
|
||||
//TODO(wonderflow) add scope support here
|
||||
func (app *Application) OAM(env *types.EnvMeta) ([]v1alpha2.Component, v1alpha2.ApplicationConfiguration, error) {
|
||||
var appConfig v1alpha2.ApplicationConfiguration
|
||||
if err := app.Validate(); err != nil {
|
||||
return nil, appConfig, err
|
||||
}
|
||||
appConfig.Name = app.Name
|
||||
appConfig.Namespace = env.Namespace
|
||||
|
||||
var components []v1alpha2.Component
|
||||
|
||||
for name := range app.Components {
|
||||
// fulfill component
|
||||
var component v1alpha2.Component
|
||||
component.Name = name
|
||||
component.Namespace = env.Namespace
|
||||
obj, err := app.GetWorkloadObject(name)
|
||||
if err != nil {
|
||||
return nil, v1alpha2.ApplicationConfiguration{}, err
|
||||
}
|
||||
labels := component.Labels
|
||||
if labels == nil {
|
||||
labels = map[string]string{types.ComponentWorkloadDefLabel: name}
|
||||
} else {
|
||||
labels[types.ComponentWorkloadDefLabel] = name
|
||||
}
|
||||
component.Labels = labels
|
||||
component.Spec.Workload.Object = obj
|
||||
components = append(components, component)
|
||||
|
||||
var appConfigComp v1alpha2.ApplicationConfigurationComponent
|
||||
appConfigComp.ComponentName = name
|
||||
//TODO(wonderflow): handle component data input/output here
|
||||
compTraits, err := app.GetComponentTraits(name)
|
||||
if err != nil {
|
||||
return nil, v1alpha2.ApplicationConfiguration{}, err
|
||||
}
|
||||
appConfigComp.Traits = compTraits
|
||||
appConfig.Spec.Components = append(appConfig.Spec.Components, appConfigComp)
|
||||
}
|
||||
return components, appConfig, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package types
|
||||
package application
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -73,7 +73,7 @@ components:
|
||||
WantWorkload string
|
||||
ExpWorklaod map[string]interface{}
|
||||
ExpWorkloadType string
|
||||
ExpTraits map[string]interface{}
|
||||
ExpTraits map[string]map[string]interface{}
|
||||
}{
|
||||
"normal case backend": {
|
||||
raw: yaml1,
|
||||
@@ -84,7 +84,7 @@ components:
|
||||
"image": "back:v1",
|
||||
},
|
||||
ExpWorkloadType: "cloneset",
|
||||
ExpTraits: map[string]interface{}{},
|
||||
ExpTraits: map[string]map[string]interface{}{},
|
||||
},
|
||||
"normal case frontend": {
|
||||
raw: yaml1,
|
||||
@@ -94,18 +94,18 @@ components:
|
||||
ExpWorklaod: map[string]interface{}{
|
||||
"image": "inanimate/echo-server",
|
||||
"env": map[string]interface{}{
|
||||
"PORT": 8080,
|
||||
"PORT": float64(8080),
|
||||
},
|
||||
},
|
||||
ExpWorkloadType: "deployment",
|
||||
ExpTraits: map[string]interface{}{
|
||||
"autoscaling": map[string]interface{}{
|
||||
"max": 10,
|
||||
"min": 1,
|
||||
ExpTraits: map[string]map[string]interface{}{
|
||||
"autoscaling": {
|
||||
"max": float64(10),
|
||||
"min": float64(1),
|
||||
},
|
||||
"rollout": map[string]interface{}{
|
||||
"rollout": {
|
||||
"strategy": "canary",
|
||||
"step": 5,
|
||||
"step": float64(5),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -144,7 +144,7 @@ components:
|
||||
var app Application
|
||||
err := yaml.Unmarshal([]byte(c.raw), &app)
|
||||
assert.NoError(t, err, caseName)
|
||||
err = app.Valid()
|
||||
err = app.Validate()
|
||||
if c.InValid {
|
||||
assert.Equal(t, c.InvalidReason, err)
|
||||
continue
|
||||
@@ -157,6 +157,6 @@ components:
|
||||
assert.Equal(t, c.ExpWorkloadType, workloadType, caseName)
|
||||
traits, err := app.GetTraits(c.WantWorkload)
|
||||
assert.NoError(t, err, caseName)
|
||||
assert.Equal(t, c.ExpTraits, traits)
|
||||
assert.Equal(t, c.ExpTraits, traits, caseName)
|
||||
}
|
||||
}
|
||||
69
pkg/application/modify.go
Normal file
69
pkg/application/modify.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (app *Application) SetWorkload(workloadName, workloadType string, workloadData map[string]interface{}) error {
|
||||
if app == nil {
|
||||
return errors.New("app is nil pointer")
|
||||
}
|
||||
if workloadData == nil {
|
||||
workloadData = make(map[string]interface{})
|
||||
}
|
||||
workloadData["name"] = strings.ToLower(workloadName)
|
||||
if app.Components == nil {
|
||||
app.Components = make(map[string]map[string]interface{})
|
||||
}
|
||||
app.Components[workloadName] = map[string]interface{}{
|
||||
workloadType: workloadData,
|
||||
}
|
||||
return app.Validate()
|
||||
}
|
||||
|
||||
func (app *Application) SetTrait(workloadName, traitType string, traitData map[string]interface{}) error {
|
||||
if app == nil {
|
||||
return errors.New("app is nil pointer")
|
||||
}
|
||||
if traitData == nil {
|
||||
traitData = make(map[string]interface{})
|
||||
}
|
||||
traitData["name"] = strings.ToLower(traitType)
|
||||
if app.Components == nil {
|
||||
app.Components = make(map[string]map[string]interface{})
|
||||
}
|
||||
comp := app.Components[workloadName]
|
||||
if comp == nil {
|
||||
comp = make(map[string]interface{})
|
||||
}
|
||||
traits, err := app.GetTraits(workloadName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
traits[traitType] = traitData
|
||||
comp[Traits] = traits
|
||||
app.Components[workloadName] = comp
|
||||
return app.Validate()
|
||||
}
|
||||
|
||||
func (app *Application) RemoveTrait(workloadName, traitType string) error {
|
||||
if app == nil {
|
||||
return errors.New("app is nil pointer")
|
||||
}
|
||||
if app.Components == nil {
|
||||
app.Components = make(map[string]map[string]interface{})
|
||||
}
|
||||
comp := app.Components[workloadName]
|
||||
if comp == nil {
|
||||
comp = make(map[string]interface{})
|
||||
}
|
||||
traits, err := app.GetTraits(workloadName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(traits, traitType)
|
||||
comp[Traits] = traits
|
||||
app.Components[workloadName] = comp
|
||||
return app.Validate()
|
||||
}
|
||||
58
pkg/application/run.go
Normal file
58
pkg/application/run.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
ctypes "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func (app *Application) Run(ctx context.Context, client client.Client, env *types.EnvMeta) error {
|
||||
components, appconfig, err := app.OAM(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, cmp := range components {
|
||||
if err = CreateOrUpdateComponent(ctx, client, cmp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return CreateOrUpdateAppConfig(ctx, client, appconfig)
|
||||
}
|
||||
|
||||
func CreateOrUpdateComponent(ctx context.Context, client client.Client, comp v1alpha2.Component) error {
|
||||
var getc v1alpha2.Component
|
||||
key := ctypes.NamespacedName{Name: comp.Name, Namespace: comp.Namespace}
|
||||
var exist = true
|
||||
if err := client.Get(ctx, key, &getc); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
exist = false
|
||||
}
|
||||
if !exist {
|
||||
return client.Create(ctx, &comp)
|
||||
}
|
||||
return client.Update(ctx, &comp)
|
||||
}
|
||||
|
||||
func CreateOrUpdateAppConfig(ctx context.Context, client client.Client, appConfig v1alpha2.ApplicationConfiguration) error {
|
||||
var geta v1alpha2.ApplicationConfiguration
|
||||
key := ctypes.NamespacedName{Name: appConfig.Name, Namespace: appConfig.Namespace}
|
||||
var exist = true
|
||||
if err := client.Get(ctx, key, &geta); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
exist = false
|
||||
}
|
||||
if !exist {
|
||||
return client.Create(ctx, &appConfig)
|
||||
}
|
||||
return client.Update(ctx, &appConfig)
|
||||
}
|
||||
@@ -325,6 +325,12 @@ func InstallCapability(client client.Client, centerName, capabilityName string,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if apiVerion, kind := cmdutil.GetApiVersionKindFromWorkload(wd); apiVerion != "" && kind != "" {
|
||||
tp.CrdInfo = &types.CrdInfo{
|
||||
ApiVersion: apiVerion,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
if err = client.Create(context.Background(), &wd); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
@@ -345,6 +351,12 @@ func InstallCapability(client client.Client, centerName, capabilityName string,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if apiVerion, kind := cmdutil.GetApiVersionKindFromTrait(td); apiVerion != "" && kind != "" {
|
||||
tp.CrdInfo = &types.CrdInfo{
|
||||
ApiVersion: apiVerion,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
if err = client.Create(context.Background(), &td); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,16 +27,15 @@ func newDeleteOptions(ioStreams cmdutil.IOStreams) *deleteOptions {
|
||||
|
||||
func newDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "app:delete [APPLICATION_NAME]",
|
||||
Use: "app:delete <APPLICATION_NAME>",
|
||||
Aliases: []string{"delete"},
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Delete OAM Applications",
|
||||
Long: "Delete OAM Applications",
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeApp,
|
||||
},
|
||||
Example: `
|
||||
vela delete frontend
|
||||
`}
|
||||
Example: "vela delete frontend"}
|
||||
}
|
||||
|
||||
// NewDeleteCommand init new command
|
||||
@@ -64,7 +63,7 @@ func NewDeleteCommand(c types.Args, ioStreams cmdutil.IOStreams, args []string)
|
||||
func (o *deleteOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if len(args) < 1 {
|
||||
return errors.New("must specify name for workload")
|
||||
return errors.New("must specify name for the app")
|
||||
}
|
||||
|
||||
namespace := o.Env.Namespace
|
||||
|
||||
@@ -59,7 +59,7 @@ func NewEnvInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
var envArgs types.EnvMeta
|
||||
ctx := context.Background()
|
||||
cmd := &cobra.Command{
|
||||
Use: "env:init",
|
||||
Use: "env:init <envName>",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Create environments",
|
||||
Long: "Create environment and switch to it",
|
||||
@@ -149,10 +149,10 @@ func ListEnvs(ctx context.Context, args []string, ioStreams cmdutil.IOStreams) e
|
||||
curEnv = types.DefaultEnvName
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.IsDir() {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
data, err := ioutil.ReadFile(filepath.Join(envDir, f.Name()))
|
||||
data, err := ioutil.ReadFile(filepath.Join(envDir, f.Name(), system.EnvConfigName))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func DeleteEnv(ctx context.Context, args []string, ioStreams cmdutil.IOStreams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.Remove(filepath.Join(envdir, envname)); err != nil {
|
||||
if err = os.RemoveAll(filepath.Join(envdir, envname)); err != nil {
|
||||
return err
|
||||
}
|
||||
ioStreams.Info(envname + " deleted")
|
||||
@@ -198,6 +198,7 @@ func CreateOrUpdateEnv(ctx context.Context, c client.Client, envArgs *types.EnvM
|
||||
return fmt.Errorf("you must specify env name for vela env:init command")
|
||||
}
|
||||
envname := args[0]
|
||||
envArgs.Name = envname
|
||||
data, err := json.Marshal(envArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -206,7 +207,9 @@ func CreateOrUpdateEnv(ctx context.Context, c client.Client, envArgs *types.EnvM
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(filepath.Join(envdir, envname), data, 0644); err != nil {
|
||||
subEnvDir := filepath.Join(envdir, envname)
|
||||
system.StatAndCreate(subEnvDir)
|
||||
if err = ioutil.WriteFile(filepath.Join(subEnvDir, system.EnvConfigName), data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
curEnvPath, err := system.GetCurrentEnvPath()
|
||||
@@ -271,11 +274,7 @@ func GetEnv() (*types.EnvMeta, error) {
|
||||
}
|
||||
|
||||
func getEnvByName(name string) (*types.EnvMeta, error) {
|
||||
envdir, err := system.GetEnvDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadFile(filepath.Join(envdir, name))
|
||||
data, err := ioutil.ReadFile(filepath.Join(system.GetEnvDirByName(name), system.EnvConfigName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ func TestENV(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &types.EnvMeta{
|
||||
Namespace: "default",
|
||||
Name: "default",
|
||||
}, gotEnv)
|
||||
|
||||
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
|
||||
exp := &types.EnvMeta{
|
||||
Namespace: "test1",
|
||||
Name: "default",
|
||||
}
|
||||
client := test.NewMockClient()
|
||||
// Create env1
|
||||
@@ -81,6 +83,7 @@ func TestENV(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &types.EnvMeta{
|
||||
Namespace: "default",
|
||||
Name: "default",
|
||||
}, gotEnv)
|
||||
|
||||
// delete env
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
"github.com/gosuri/uitable"
|
||||
"strings"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
@@ -26,6 +27,7 @@ func NewAppsCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
cmd := &cobra.Command{
|
||||
Use: "app:ls",
|
||||
Aliases: []string{"ls"},
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "List applications",
|
||||
Long: "List applications with workloads, traits, status and created time",
|
||||
|
||||
@@ -18,14 +18,15 @@ import (
|
||||
func NewAppShowCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
cmd := &cobra.Command{
|
||||
Use: "app:show",
|
||||
Use: "app:show <APPLICATION-NAME>",
|
||||
Aliases: []string{"show"},
|
||||
Short: "get detail spec of your app",
|
||||
Long: "get detail spec of your app, including its workload and trait",
|
||||
Example: `vela app:show <APPLICATION-NAME>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
argsLength := len(args)
|
||||
if argsLength == 0 {
|
||||
ioStreams.Errorf("Hint: please specify an application")
|
||||
ioStreams.Errorf("Hint: please specify the application name")
|
||||
os.Exit(1)
|
||||
}
|
||||
appName := args[0]
|
||||
@@ -79,7 +80,7 @@ func showApplication(ctx context.Context, c client.Client, cmd *cobra.Command, e
|
||||
}
|
||||
if component.Labels == nil {
|
||||
return fmt.Errorf("Can't get workloadDef, please check component %s label \"%s\" is correct.",
|
||||
componentName, ComponentWorkloadDefLabel)
|
||||
componentName, types.ComponentWorkloadDefLabel)
|
||||
}
|
||||
|
||||
traitDefinitions := cmdutil.ListTraitDefinitionsByApplicationConfiguration(application)
|
||||
@@ -24,7 +24,8 @@ type ApplicationStatusMeta struct {
|
||||
func NewAppStatusCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
cmd := &cobra.Command{
|
||||
Use: "app:status",
|
||||
Use: "app:status <APPLICATION-NAME>",
|
||||
Aliases: []string{"status"},
|
||||
Short: "get status of an application",
|
||||
Long: "get status of an application, including its workload and trait",
|
||||
Example: `vela app:status <APPLICATION-NAME>`,
|
||||
|
||||
@@ -2,38 +2,33 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
mycue "github.com/cloud-native-application/rudrx/pkg/cue"
|
||||
"github.com/cloud-native-application/rudrx/pkg/application"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/pkg/plugins"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/api/types"
|
||||
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type commandOptions struct {
|
||||
Template types.Capability
|
||||
Component corev1alpha2.Component
|
||||
AppConfig corev1alpha2.ApplicationConfiguration
|
||||
Client client.Client
|
||||
TraitAlias string
|
||||
Detach bool
|
||||
Env *types.EnvMeta
|
||||
|
||||
workloadName string
|
||||
appName string
|
||||
staging bool
|
||||
app *application.Application
|
||||
cmdutil.IOStreams
|
||||
}
|
||||
|
||||
@@ -56,14 +51,14 @@ func AddTraitCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Attach " + name + " trait to an app",
|
||||
Long: "Attach " + name + " trait to an app",
|
||||
Example: `vela scale frontend --max=5`,
|
||||
Example: "vela " + name + " frontend",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Client = newClient
|
||||
if err := o.Complete(cmd, args, ctx); err != nil {
|
||||
if err := o.AddOrUpdateTrait(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Run(cmd, ctx)
|
||||
@@ -76,6 +71,8 @@ func AddTraitCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.
|
||||
for _, v := range tmp.Parameters {
|
||||
types.SetFlagBy(pluginCmd, v)
|
||||
}
|
||||
pluginCmd.Flags().StringP(App, "a", "", "create or add into an existing application group")
|
||||
pluginCmd.Flags().BoolP(Staging, "s", false, "only save changes locally without real update application")
|
||||
|
||||
o.Template = tmp
|
||||
parentCmd.AddCommand(pluginCmd)
|
||||
@@ -83,35 +80,32 @@ func AddTraitCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *commandOptions) Complete(cmd *cobra.Command, args []string, ctx context.Context) error {
|
||||
argsLength := len(args)
|
||||
var appName string
|
||||
|
||||
c := o.Client
|
||||
|
||||
namespace := o.Env.Namespace
|
||||
|
||||
if argsLength < 1 {
|
||||
func (o *commandOptions) Prepare(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("please specify the name of the app")
|
||||
}
|
||||
o.workloadName = args[0]
|
||||
if app := cmd.Flag(App).Value.String(); app != "" {
|
||||
o.appName = app
|
||||
} else {
|
||||
o.appName = o.workloadName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get AppConfig
|
||||
// TODO(wonderflow): appName is Component Name here, check if it's has appset with a different name
|
||||
appName = args[0]
|
||||
if err := c.Get(ctx, client.ObjectKey{Namespace: o.Env.Namespace, Name: appName}, &o.AppConfig); err != nil {
|
||||
func (o *commandOptions) AddOrUpdateTrait(cmd *cobra.Command, args []string) error {
|
||||
if err := o.Prepare(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get component
|
||||
var component corev1alpha2.Component
|
||||
if err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: appName}, &component); err != nil {
|
||||
app, err := application.Load(o.Env.Name, o.appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var traitType = o.Template.Name
|
||||
traitData, err := app.GetTraitsByType(o.workloadName, traitType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var traitData = make(map[string]interface{})
|
||||
|
||||
var tp = o.Template.Name
|
||||
|
||||
for _, v := range o.Template.Parameters {
|
||||
flagSet := cmd.Flag(v.Name)
|
||||
switch v.Type {
|
||||
@@ -128,43 +122,11 @@ func (o *commandOptions) Complete(cmd *cobra.Command, args []string, ctx context
|
||||
traitData[v.Name] = d
|
||||
}
|
||||
}
|
||||
|
||||
jsondata, err := mycue.Eval(o.Template.DefinitionPath, tp, traitData)
|
||||
if err != nil {
|
||||
if err = app.SetTrait(o.workloadName, traitType, traitData); err != nil {
|
||||
return err
|
||||
}
|
||||
var obj = make(map[string]interface{})
|
||||
if err = json.Unmarshal([]byte(jsondata), &obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pvd := fieldpath.Pave(obj)
|
||||
// metadata.name needs to be in lower case.
|
||||
pvd.SetString("metadata.name", strings.ToLower(fmt.Sprintf("%s-%s-trait", appName, o.Template.Name)))
|
||||
curObj := &unstructured.Unstructured{Object: pvd.UnstructuredContent()}
|
||||
var updated bool
|
||||
for ic, c := range o.AppConfig.Spec.Components {
|
||||
if c.ComponentName != appName {
|
||||
continue
|
||||
}
|
||||
for it, t := range c.Traits {
|
||||
g, v, k := GetGVKFromRawExtension(t.Trait)
|
||||
|
||||
// TODO(wonderflow): we should get GVK from DefinitionPath instead of assuming template object contains
|
||||
gvk := curObj.GroupVersionKind()
|
||||
if gvk.Group == g && gvk.Version == v && gvk.Kind == k {
|
||||
updated = true
|
||||
c.Traits[it] = corev1alpha2.ComponentTrait{Trait: runtime.RawExtension{Object: curObj}}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !updated {
|
||||
c.Traits = append(c.Traits, corev1alpha2.ComponentTrait{Trait: runtime.RawExtension{Object: curObj}})
|
||||
}
|
||||
o.AppConfig.Spec.Components[ic] = c
|
||||
break
|
||||
}
|
||||
return nil
|
||||
o.app = app
|
||||
return app.Save(o.Env.Name, o.appName)
|
||||
}
|
||||
|
||||
func AddTraitDetachCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error {
|
||||
@@ -182,14 +144,14 @@ func AddTraitDetachCommands(parentCmd *cobra.Command, c types.Args, ioStreams cm
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Detach " + name + " trait from an app",
|
||||
Long: "Detach " + name + " trait from an app",
|
||||
Example: `vela scale:detach frontend`,
|
||||
Example: "vela " + name + ":detach frontend",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Client = newClient
|
||||
if err := o.DetachTrait(cmd, args, ctx); err != nil {
|
||||
if err := o.DetachTrait(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Run(cmd, ctx)
|
||||
@@ -206,48 +168,36 @@ func AddTraitDetachCommands(parentCmd *cobra.Command, c types.Args, ioStreams cm
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *commandOptions) DetachTrait(cmd *cobra.Command, args []string, ctx context.Context) error {
|
||||
argsLength := len(args)
|
||||
if argsLength < 1 {
|
||||
return errors.New("please specify the name of the app")
|
||||
}
|
||||
c := o.Client
|
||||
namespace := o.Env.Namespace
|
||||
|
||||
var appName = args[0]
|
||||
if err := c.Get(ctx, client.ObjectKey{Namespace: o.Env.Namespace, Name: appName}, &o.AppConfig); err != nil {
|
||||
func (o *commandOptions) DetachTrait(cmd *cobra.Command, args []string) error {
|
||||
if err := o.Prepare(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiVersion, kind, err := cmdutil.GetTraitApiVersionKind(ctx, c, namespace, o.TraitAlias)
|
||||
app, err := application.Load(o.Env.Name, o.appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, com := range o.AppConfig.Spec.Components {
|
||||
if com.ComponentName != appName {
|
||||
continue
|
||||
}
|
||||
var traits []corev1alpha2.ComponentTrait
|
||||
for _, tr := range com.Traits {
|
||||
a, k := tr.Trait.Object.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind()
|
||||
if a == apiVersion && k == kind {
|
||||
continue
|
||||
}
|
||||
traits = append(traits, tr)
|
||||
}
|
||||
o.AppConfig.Spec.Components[i].Traits = traits
|
||||
var traitType = o.Template.Name
|
||||
if err = app.RemoveTrait(o.workloadName, traitType); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return app.Save(o.Env.Name, o.appName)
|
||||
}
|
||||
|
||||
func (o *commandOptions) Run(cmd *cobra.Command, ctx context.Context) error {
|
||||
if o.Detach {
|
||||
o.Info("Detaching trait from app", o.Component.Name)
|
||||
o.Infof("Detaching %s from app %s\n", o.Template.Name, o.workloadName)
|
||||
} else {
|
||||
o.Info("Applying trait for app", o.Component.Name)
|
||||
o.Infof("Adding %s for app %s \n", o.Template.Name, o.workloadName)
|
||||
}
|
||||
c := o.Client
|
||||
err := c.Update(ctx, &o.AppConfig)
|
||||
staging, err := strconv.ParseBool(cmd.Flag(Staging).Value.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if staging {
|
||||
o.Info("Staging saved")
|
||||
return nil
|
||||
}
|
||||
err = o.app.Run(ctx, o.Client, o.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,11 +107,19 @@ func GetTraitApiVersionKind(ctx context.Context, c client.Client, namespace stri
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
apiVersion := t.Annotations["oam.appengine.info/apiVersion"]
|
||||
kind := t.Annotations["oam.appengine.info/kind"]
|
||||
apiVersion := t.Annotations[types.AnnApiVersion]
|
||||
kind := t.Annotations[types.AnnKind]
|
||||
return apiVersion, kind, nil
|
||||
}
|
||||
|
||||
func GetApiVersionKindFromTrait(td corev1alpha2.TraitDefinition) (string, string) {
|
||||
return td.Annotations[types.AnnApiVersion], td.Annotations[types.AnnKind]
|
||||
}
|
||||
|
||||
func GetApiVersionKindFromWorkload(td corev1alpha2.WorkloadDefinition) (string, string) {
|
||||
return td.Annotations[types.AnnApiVersion], td.Annotations[types.AnnKind]
|
||||
}
|
||||
|
||||
func GetWorkloadNameAliasKind(ctx context.Context, c client.Client, namespace string, workloadName string) (string, string, string) {
|
||||
var name, alias, kind string
|
||||
|
||||
|
||||
@@ -2,52 +2,39 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/pkg/utils/system"
|
||||
|
||||
"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"
|
||||
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
mycue "github.com/cloud-native-application/rudrx/pkg/cue"
|
||||
|
||||
corev1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
|
||||
)
|
||||
|
||||
// ComponentWorkloadDefLabel indicate which workloaddefinition generate from
|
||||
const ComponentWorkloadDefLabel = "vela.oam.dev/workloadDef"
|
||||
const Staging = "staging"
|
||||
const App = "app"
|
||||
|
||||
type runOptions struct {
|
||||
Template types.Capability
|
||||
Env *types.EnvMeta
|
||||
workloadName string
|
||||
client client.Client
|
||||
app *types.Application
|
||||
cmdutil.IOStreams
|
||||
app *application.Application
|
||||
appName string
|
||||
staging bool
|
||||
util.IOStreams
|
||||
}
|
||||
|
||||
func newRunOptions(ioStreams cmdutil.IOStreams) *runOptions {
|
||||
func newRunOptions(ioStreams util.IOStreams) *runOptions {
|
||||
return &runOptions{IOStreams: ioStreams}
|
||||
}
|
||||
|
||||
func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.IOStreams) error {
|
||||
func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams util.IOStreams) error {
|
||||
templates, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,7 +49,7 @@ func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdut
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Run " + name + " workloads",
|
||||
Long: "Run " + name + " workloads",
|
||||
Example: `vela deployment:run frontend -i nginx:latest`,
|
||||
Example: "vela " + name + ":run frontend",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
||||
if err != nil {
|
||||
@@ -82,6 +69,8 @@ func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdut
|
||||
for _, v := range tmp.Parameters {
|
||||
types.SetFlagBy(pluginCmd, v)
|
||||
}
|
||||
pluginCmd.Flags().StringP(App, "a", "", "create or add into an existing application group")
|
||||
pluginCmd.Flags().BoolP(Staging, "s", false, "only save changes locally without real update application")
|
||||
|
||||
o.Template = tmp
|
||||
parentCmd.AddCommand(pluginCmd)
|
||||
@@ -90,21 +79,25 @@ func AddWorkloadCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdut
|
||||
}
|
||||
|
||||
func (o *runOptions) Complete(cmd *cobra.Command, args []string, ctx context.Context) error {
|
||||
|
||||
argsLength := len(args)
|
||||
|
||||
if argsLength < 1 {
|
||||
return errors.New("must specify name for workload")
|
||||
}
|
||||
|
||||
workloadName := args[0]
|
||||
// TODO(wonderflow): load application from file
|
||||
var app = &types.Application{Name: workloadName}
|
||||
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
|
||||
}
|
||||
|
||||
if app.Components == nil {
|
||||
app.Components = make(map[string]map[string]interface{})
|
||||
}
|
||||
tp, workloadData, err := app.GetWorkload(workloadName)
|
||||
tp, workloadData, err := app.GetWorkload(o.workloadName)
|
||||
if err != nil {
|
||||
// Not exist
|
||||
tp = o.Template.Name
|
||||
@@ -127,52 +120,25 @@ func (o *runOptions) Complete(cmd *cobra.Command, args []string, ctx context.Con
|
||||
workloadData[v.Name] = d
|
||||
}
|
||||
}
|
||||
workloadData["name"] = strings.ToLower(workloadName)
|
||||
app.Components[workloadName] = map[string]interface{}{
|
||||
tp: workloadData,
|
||||
}
|
||||
o.workloadName = workloadName
|
||||
o.app = app
|
||||
appDir, _ := system.GetApplicationDir()
|
||||
out, err := yaml.Marshal(app)
|
||||
if err != nil {
|
||||
if err = app.SetWorkload(o.workloadName, tp, workloadData); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filepath.Join(appDir, workloadName), out, 0644)
|
||||
o.app = app
|
||||
return app.Save(o.Env.Name, o.appName)
|
||||
}
|
||||
|
||||
func (o *runOptions) Run(cmd *cobra.Command) error {
|
||||
var component corev1alpha2.Component
|
||||
var appconfig corev1alpha2.ApplicationConfiguration
|
||||
tp, data, _ := o.app.GetWorkload(o.workloadName)
|
||||
jsondata, err := mycue.Eval(o.Template.DefinitionPath, tp, data)
|
||||
staging, err := strconv.ParseBool(cmd.Flag(Staging).Value.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var obj = make(map[string]interface{})
|
||||
if err = json.Unmarshal([]byte(jsondata), &obj); err != nil {
|
||||
return err
|
||||
if staging {
|
||||
o.Info("Staging saved")
|
||||
return nil
|
||||
}
|
||||
|
||||
component.Spec.Workload.Object = &unstructured.Unstructured{Object: obj}
|
||||
component.Name = o.workloadName
|
||||
component.Namespace = o.Env.Namespace
|
||||
component.Labels = map[string]string{ComponentWorkloadDefLabel: o.workloadName}
|
||||
|
||||
appconfig.Name = o.workloadName
|
||||
appconfig.Namespace = o.Env.Namespace
|
||||
appconfig.Spec.Components = append(appconfig.Spec.Components, corev1alpha2.ApplicationConfigurationComponent{ComponentName: o.workloadName})
|
||||
|
||||
//TODO(wonderflow): we should also support update here
|
||||
|
||||
o.Infof("Creating AppConfig %s\n", appconfig.Name)
|
||||
err = o.client.Create(context.Background(), &component)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create component err: %s", err)
|
||||
}
|
||||
err = o.client.Create(context.Background(), &appconfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create appconfig err %s", 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")
|
||||
return nil
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
|
||||
|
||||
"github.com/cloud-native-application/rudrx/pkg/utils/system"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
@@ -47,6 +49,12 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c client.Cli
|
||||
fmt.Printf("[WARN]handle template %s: %v\n", wd.Name, err)
|
||||
continue
|
||||
}
|
||||
if apiVerion, kind := cmdutil.GetApiVersionKindFromWorkload(wd); apiVerion != "" && kind != "" {
|
||||
tmp.CrdInfo = &types.CrdInfo{
|
||||
ApiVersion: apiVerion,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
templates = append(templates, tmp)
|
||||
}
|
||||
return templates, nil
|
||||
@@ -66,6 +74,12 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c client.Client
|
||||
fmt.Printf("[WARN]handle template %s: %v\n", td.Name, err)
|
||||
continue
|
||||
}
|
||||
if apiVerion, kind := cmdutil.GetApiVersionKindFromTrait(td); apiVerion != "" && kind != "" {
|
||||
tmp.CrdInfo = &types.CrdInfo{
|
||||
ApiVersion: apiVerion,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
templates = append(templates, tmp)
|
||||
}
|
||||
return templates, nil
|
||||
|
||||
@@ -13,6 +13,19 @@ import (
|
||||
"github.com/cloud-native-application/rudrx/pkg/utils/system"
|
||||
)
|
||||
|
||||
func LoadCapabilityByName(name string) (types.Capability, error) {
|
||||
caps, err := LoadAllInstalledCapability()
|
||||
if err != nil {
|
||||
return types.Capability{}, err
|
||||
}
|
||||
for _, c := range caps {
|
||||
if c.Name == name {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
return types.Capability{}, fmt.Errorf("%s not found", name)
|
||||
}
|
||||
|
||||
func LoadAllInstalledCapability() ([]types.Capability, error) {
|
||||
workloads, err := LoadInstalledCapabilityWithType(types.TypeWorkload)
|
||||
if err != nil {
|
||||
|
||||
@@ -39,14 +39,6 @@ func GetRepoConfig() (string, error) {
|
||||
return filepath.Join(home, "config.yaml"), nil
|
||||
}
|
||||
|
||||
func GetApplicationDir() (string, error) {
|
||||
home, err := GetVelaHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, "applications"), nil
|
||||
}
|
||||
|
||||
func GetCapabilityDir() (string, error) {
|
||||
home, err := GetVelaHomeDir()
|
||||
if err != nil {
|
||||
@@ -75,9 +67,6 @@ func InitDirs() error {
|
||||
if err := InitCapabilityDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := InitApplicationDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := InitCapCenterDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,22 +89,22 @@ func InitCapabilityDir() error {
|
||||
return StatAndCreate(dir)
|
||||
}
|
||||
|
||||
func InitApplicationDir() error {
|
||||
dir, err := GetApplicationDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return StatAndCreate(dir)
|
||||
func GetApplicationDir(envName string) (string, error) {
|
||||
appDir := filepath.Join(GetEnvDirByName(envName), "applications")
|
||||
return appDir, StatAndCreate(appDir)
|
||||
}
|
||||
|
||||
const EnvConfigName = "config.json"
|
||||
|
||||
func InitDefaultEnv() error {
|
||||
envDir, err := GetEnvDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
StatAndCreate(envDir)
|
||||
data, _ := json.Marshal(&types.EnvMeta{Namespace: types.DefaultEnvName})
|
||||
if err = ioutil.WriteFile(filepath.Join(envDir, types.DefaultEnvName), data, 0644); err != nil {
|
||||
defaultEnvDir := filepath.Join(envDir, types.DefaultEnvName)
|
||||
StatAndCreate(defaultEnvDir)
|
||||
data, _ := json.Marshal(&types.EnvMeta{Namespace: types.DefaultAppNamespace, Name: types.DefaultEnvName})
|
||||
if err = ioutil.WriteFile(filepath.Join(defaultEnvDir, EnvConfigName), data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
curEnvPath, err := GetCurrentEnvPath()
|
||||
@@ -134,3 +123,8 @@ func StatAndCreate(dir string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetEnvDirByName(name string) string {
|
||||
envdir, _ := GetEnvDir()
|
||||
return filepath.Join(envdir, name)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user