mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-06 11:41:41 +00:00
* Feat(rollout): fill rolloutBatches if empty when scale up/down (#2569) * Feat: fill rolloutBatches if empty * Fix: fix unit-test * Test: add more test Fix: lint Fix: fix lint * Update release.yml (#2537) * Feat: add registry, merge registry and cap center (#2528) * Feat: add registry command * Refactor: comp/trait command combine with registry * Feat: refactor `vela comp/trait` * Fix: import * Fix: fix if type is autodetects.core.oam.dev * Fix: fix list from url * Fix: test * Feat: add test * Fix: remove dup test * Fix: test * Fix: test * Fix: fix label filter * Fix: reviewable * Fix test * fix personal repo in test * Fix test * Fix test * add some boundary check * reviewable * Fix: fix nocalhost trait (#2577) * fix incorrect addon status (#2576) * Fix(cli): client-side throttling in vela CLI (#2581) * fix cli throttling * fix import * set to a lower value * remove addon with no defs (#2574) * Feat: vela logs support multicluster (#2593) * Feat: add basic multiple cluster logs * fix context * Fix select style * Fix select style * remove useless env * fix naming * Feat: vela cluster support use ocm to join/list/detach cluster (#2599) * Feat: add render component and apply component remaining (#2587) * Feat: add render component and apply component remaining * fix ut * fix e2e * allow import package in custom status cue template (#2585) Co-authored-by: chwetion <chwetion@foxmail.com> * Fix: abnormal aux name (#2612) * Feat: store workflow step def properties in cm (#2592) * Fix: fix notification def * Feat: store workflow step def properties in cm * fix ci * fix data race * Fix: change Initializer to Application for addon Observability (#2615) In this doc, updated the Observability implementation from initializer to Application. I also store definitions as it's not well stored in vela-templates/addons/observability * Fix: fix backport param (#2611) * Fix: add owner reference in workflow context cm (#2573) * Fix: add owner reference in workflow context cm * fix ci * delete useless test case * Fix: op.delete bugs (#2622) * Fix: op.delete some bugs * Fix: app status update error Fix: make reviewable * Fix: show reconcile error log (#2626) * Feat: add reconcile timeout configuration for vela-core (#2630) * Fix: patch status retry while conflict happens (#2629) * Fix: allow definition schema cm can be same name in different definition type (#2618) * Fix: fix definition schema cm name * fix ut * fix ut * fix show * add switch default case * Feat: remove envbinding policy into workflow (#2556) Fix: add more test * Feat: add vela prob to test cluster (#2635) * Fix: upgrade stern lib to avoid panic for vela logs (#2650) * Fix: filter loggable workload in vela logs (#2651) * Fix: filter loggable workload in vela logs * reviewable * Feat: add vela exec for multi cluster (#2299) fix support vela exec * Fix: health check will check for multiclusters (#2645) * Fix: minor fix for vela cli printing (#2655) * Fix: minor fix for vela cli printing * add dockerfile go mod cache * Feat: support apiserver-related multicluster features (#2625) * Feat: remove envbinding policy into workflow Feat: add support for env change (env gc) Fix: fix rollout timeout setting bug * Feat: support disable trait and env without workflow * Fix: add hint for replaced value Co-authored-by: wyike <wangyike_wyk@163.com> Co-authored-by: basefas <basefas@hotmail.com> Co-authored-by: qiaozp <47812250+chivalryq@users.noreply.github.com> Co-authored-by: Tianxin Dong <dongtianxin.tx@alibaba-inc.com> Co-authored-by: yangsoon <yangsoonlx@gmail.com> Co-authored-by: Chwetion <137953601@qq.com> Co-authored-by: chwetion <chwetion@foxmail.com> Co-authored-by: Jian.Li <74582607+leejanee@users.noreply.github.com> Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com> Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
335 lines
8.5 KiB
Go
335 lines
8.5 KiB
Go
/*
|
|
Copyright 2021 The KubeVela Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package context
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"cuelang.org/go/cue"
|
|
"github.com/pkg/errors"
|
|
corev1 "k8s.io/api/core/v1"
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/utils/pointer"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
|
"github.com/oam-dev/kubevela/pkg/cue/model"
|
|
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
)
|
|
|
|
const (
|
|
// ConfigMapKeyComponents is the key in ConfigMap Data field for containing data of components
|
|
ConfigMapKeyComponents = "components"
|
|
// ConfigMapKeyVars is the key in ConfigMap Data field for containing data of variable
|
|
ConfigMapKeyVars = "vars"
|
|
// AnnotationStartTimestamp is the annotation key of the workflow start timestamp
|
|
AnnotationStartTimestamp = "vela.io/startTime"
|
|
)
|
|
|
|
// WorkflowContext is workflow context.
|
|
type WorkflowContext struct {
|
|
cli client.Client
|
|
store corev1.ConfigMap
|
|
components map[string]*ComponentManifest
|
|
vars *value.Value
|
|
modified bool
|
|
}
|
|
|
|
// GetComponent Get ComponentManifest from workflow context.
|
|
func (wf *WorkflowContext) GetComponent(name string) (*ComponentManifest, error) {
|
|
component, ok := wf.components[name]
|
|
if !ok {
|
|
return nil, errors.Errorf("component %s not found in application", name)
|
|
}
|
|
return component, nil
|
|
}
|
|
|
|
// GetComponents Get All ComponentManifest from workflow context.
|
|
func (wf *WorkflowContext) GetComponents() map[string]*ComponentManifest {
|
|
return wf.components
|
|
}
|
|
|
|
// PatchComponent patch component with value.
|
|
func (wf *WorkflowContext) PatchComponent(name string, patchValue *value.Value) error {
|
|
component, err := wf.GetComponent(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := component.Patch(patchValue); err != nil {
|
|
return err
|
|
}
|
|
wf.modified = true
|
|
return nil
|
|
}
|
|
|
|
// GetVar get variable from workflow context.
|
|
func (wf *WorkflowContext) GetVar(paths ...string) (*value.Value, error) {
|
|
return wf.vars.LookupValue(paths...)
|
|
}
|
|
|
|
// SetVar set variable to workflow context.
|
|
func (wf *WorkflowContext) SetVar(v *value.Value, paths ...string) error {
|
|
str, err := v.String()
|
|
if err != nil {
|
|
return errors.WithMessage(err, "compile var")
|
|
}
|
|
if err := wf.vars.FillRaw(str, paths...); err != nil {
|
|
return err
|
|
}
|
|
if err := wf.vars.Error(); err != nil {
|
|
return err
|
|
}
|
|
wf.modified = true
|
|
return nil
|
|
}
|
|
|
|
// MakeParameter make 'value' with interface{}
|
|
func (wf *WorkflowContext) MakeParameter(parameter interface{}) (*value.Value, error) {
|
|
var s = "{}"
|
|
if parameter != nil {
|
|
bt, err := json.Marshal(parameter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s = string(bt)
|
|
}
|
|
|
|
return wf.vars.MakeValue(s)
|
|
}
|
|
|
|
// Commit the workflow context and persist it's content.
|
|
func (wf *WorkflowContext) Commit() error {
|
|
if !wf.modified {
|
|
return nil
|
|
}
|
|
if err := wf.writeToStore(); err != nil {
|
|
return err
|
|
}
|
|
if err := wf.sync(); err != nil {
|
|
return errors.WithMessagef(err, "save context to configMap(%s/%s)", wf.store.Namespace, wf.store.Name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (wf *WorkflowContext) writeToStore() error {
|
|
varStr, err := wf.vars.String()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
jsonObject := map[string]string{}
|
|
for name, comp := range wf.components {
|
|
s, err := comp.string()
|
|
if err != nil {
|
|
return errors.WithMessagef(err, "encode component %s ", name)
|
|
}
|
|
jsonObject[name] = s
|
|
}
|
|
|
|
wf.store.Data = map[string]string{
|
|
ConfigMapKeyComponents: string(util.MustJSONMarshal(jsonObject)),
|
|
ConfigMapKeyVars: varStr,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (wf *WorkflowContext) sync() error {
|
|
ctx := context.Background()
|
|
if err := wf.cli.Update(ctx, &wf.store); err != nil {
|
|
if kerrors.IsNotFound(err) {
|
|
return wf.cli.Create(ctx, &wf.store)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadFromConfigMap recover workflow context from configMap.
|
|
func (wf *WorkflowContext) LoadFromConfigMap(cm corev1.ConfigMap) error {
|
|
data := cm.Data
|
|
componentsJs := map[string]string{}
|
|
|
|
if err := json.Unmarshal([]byte(data[ConfigMapKeyComponents]), &componentsJs); err != nil {
|
|
return errors.WithMessage(err, "decode components")
|
|
}
|
|
wf.components = map[string]*ComponentManifest{}
|
|
for name, compJs := range componentsJs {
|
|
cm := new(ComponentManifest)
|
|
if err := cm.unmarshal(compJs); err != nil {
|
|
return errors.WithMessagef(err, "unmarshal component(%s) manifest", name)
|
|
}
|
|
wf.components[name] = cm
|
|
}
|
|
var err error
|
|
wf.vars, err = value.NewValue(data[ConfigMapKeyVars], nil, "")
|
|
if err != nil {
|
|
return errors.WithMessage(err, "decode vars")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StoreRef return the store reference of workflow context.
|
|
func (wf *WorkflowContext) StoreRef() *corev1.ObjectReference {
|
|
return &corev1.ObjectReference{
|
|
APIVersion: wf.store.APIVersion,
|
|
Kind: wf.store.Kind,
|
|
Name: wf.store.Name,
|
|
UID: wf.store.UID,
|
|
}
|
|
}
|
|
|
|
// ComponentManifest contains resources rendered from an application component.
|
|
type ComponentManifest struct {
|
|
Workload model.Instance
|
|
Auxiliaries []model.Instance
|
|
}
|
|
|
|
// Patch the ComponentManifest with value
|
|
func (comp *ComponentManifest) Patch(patchValue *value.Value) error {
|
|
pInst, err := model.NewOther(patchValue.CueValue())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return comp.Workload.Unify(pInst)
|
|
}
|
|
|
|
type componentMould struct {
|
|
StandardWorkload string
|
|
Traits []string
|
|
}
|
|
|
|
func (comp *ComponentManifest) string() (string, error) {
|
|
cm := componentMould{
|
|
StandardWorkload: comp.Workload.String(),
|
|
}
|
|
for _, aux := range comp.Auxiliaries {
|
|
cm.Traits = append(cm.Traits, aux.String())
|
|
}
|
|
js, err := json.Marshal(cm)
|
|
return string(js), err
|
|
}
|
|
|
|
func (comp *ComponentManifest) unmarshal(v string) error {
|
|
|
|
cm := componentMould{}
|
|
if err := json.Unmarshal([]byte(v), &cm); err != nil {
|
|
return err
|
|
}
|
|
var r cue.Runtime
|
|
wlInst, err := r.Compile("workload", cm.StandardWorkload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
wl, err := model.NewBase(wlInst.Value())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
comp.Workload = wl
|
|
for _, s := range cm.Traits {
|
|
auxInst, err := r.Compile("-", s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
aux, err := model.NewOther(auxInst.Value())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
comp.Auxiliaries = append(comp.Auxiliaries, aux)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewContext new workflow context without initialize data.
|
|
func NewContext(cli client.Client, ns, app string, appUID types.UID) (Context, error) {
|
|
wfCtx, err := newContext(cli, ns, app, appUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return wfCtx, wfCtx.Commit()
|
|
}
|
|
|
|
func newContext(cli client.Client, ns, app string, appUID types.UID) (*WorkflowContext, error) {
|
|
var (
|
|
ctx = context.Background()
|
|
store corev1.ConfigMap
|
|
)
|
|
store.Name = generateStoreName(app)
|
|
store.Namespace = ns
|
|
store.SetOwnerReferences([]metav1.OwnerReference{
|
|
{
|
|
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
|
Kind: v1beta1.ApplicationKind,
|
|
Name: app,
|
|
UID: appUID,
|
|
Controller: pointer.BoolPtr(true),
|
|
},
|
|
})
|
|
if err := cli.Get(ctx, client.ObjectKey{Name: store.Name, Namespace: store.Namespace}, &store); err != nil {
|
|
if kerrors.IsNotFound(err) {
|
|
if err := cli.Create(ctx, &store); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
store.Annotations = map[string]string{
|
|
AnnotationStartTimestamp: time.Now().String(),
|
|
}
|
|
wfCtx := &WorkflowContext{
|
|
cli: cli,
|
|
store: store,
|
|
components: map[string]*ComponentManifest{},
|
|
modified: true,
|
|
}
|
|
var err error
|
|
wfCtx.vars, err = value.NewValue("", nil, "")
|
|
|
|
return wfCtx, err
|
|
}
|
|
|
|
// LoadContext load workflow context from store.
|
|
func LoadContext(cli client.Client, ns, app string) (Context, error) {
|
|
var store corev1.ConfigMap
|
|
if err := cli.Get(context.Background(), client.ObjectKey{
|
|
Namespace: ns,
|
|
Name: generateStoreName(app),
|
|
}, &store); err != nil {
|
|
return nil, err
|
|
}
|
|
ctx := &WorkflowContext{
|
|
cli: cli,
|
|
store: store,
|
|
}
|
|
if err := ctx.LoadFromConfigMap(store); err != nil {
|
|
return nil, err
|
|
}
|
|
return ctx, nil
|
|
}
|
|
|
|
func generateStoreName(app string) string {
|
|
return fmt.Sprintf("workflow-%s-context", app)
|
|
}
|