mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-05 11:11:28 +00:00
* Fix(context): add support for context.appLables and context.appAnnotations (#3463) Signed-off-by: zxbyoyoyo <596908030@qq.com> Signed-off-by: 朱晓兵 <596908030@qq.com> * Fix: unit test Signed-off-by: 朱晓兵 <596908030@qq.com> * Fix: recover deleted field Signed-off-by: 朱晓兵 <596908030@qq.com>
329 lines
9.6 KiB
Go
329 lines
9.6 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 process
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
|
"github.com/oam-dev/kubevela/pkg/cue/model"
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
)
|
|
|
|
// Context defines Rendering Context Interface
|
|
type Context interface {
|
|
SetBase(base model.Instance) error
|
|
AppendAuxiliaries(auxiliaries ...Auxiliary) error
|
|
Output() (model.Instance, []Auxiliary)
|
|
BaseContextFile() string
|
|
ExtendedContextFile() string
|
|
BaseContextLabels() map[string]string
|
|
SetParameters(params map[string]interface{})
|
|
PushData(key string, data interface{})
|
|
GetCtx() context.Context
|
|
SetCtx(context.Context)
|
|
}
|
|
|
|
// Auxiliary are objects rendered by definition template.
|
|
// the format for auxiliary resource is always: `outputs.<resourceName>`, it can be auxiliary workload or trait
|
|
type Auxiliary struct {
|
|
Ins model.Instance
|
|
// Type will be used to mark definition label for OAM runtime to get the CRD
|
|
// It's now required for trait and main workload object. Extra workload CR object will not have the type.
|
|
Type string
|
|
|
|
// Workload or trait with multiple `outputs` will have a name, if name is empty, than it's the main of this type.
|
|
Name string
|
|
}
|
|
|
|
type templateContext struct {
|
|
// name is the component name of Application
|
|
name string
|
|
// appName is the name of Application
|
|
appName string
|
|
// appRevision is the revision name of Application
|
|
appRevision string
|
|
workflowName string
|
|
publishVersion string
|
|
configs []map[string]string
|
|
base model.Instance
|
|
auxiliaries []Auxiliary
|
|
// namespace is the namespace of Application which is used to set the namespace for Crossplane connection secret,
|
|
// ComponentDefinition/TratiDefinition OpenAPI v3 schema
|
|
namespace string
|
|
// parameters is used to store the properties passed into the current component
|
|
parameters map[string]interface{}
|
|
// outputSecretName is used to store all secret names which are generated by cloud resource components
|
|
outputSecretName string
|
|
// requiredSecrets is used to store all secret names which are generated by cloud resource components and required by current component
|
|
requiredSecrets []RequiredSecrets
|
|
|
|
baseHooks []BaseHook
|
|
auxiliaryHooks []AuxiliaryHook
|
|
|
|
components []common.ApplicationComponent
|
|
|
|
data map[string]interface{}
|
|
|
|
// appLabels is the labels of Application
|
|
appLabels map[string]string
|
|
// appAnnotations is the annotations of Application
|
|
appAnnotations map[string]string
|
|
|
|
ctx context.Context
|
|
}
|
|
|
|
// RequiredSecrets is used to store all secret names which are generated by cloud resource components and required by current component
|
|
type RequiredSecrets struct {
|
|
Namespace string
|
|
Name string
|
|
ContextName string
|
|
Data map[string]interface{}
|
|
}
|
|
|
|
// ContextData is the core data of process context
|
|
type ContextData struct {
|
|
Namespace string
|
|
AppName string
|
|
CompName string
|
|
AppRevisionName string
|
|
WorkflowName string
|
|
PublishVersion string
|
|
|
|
Ctx context.Context
|
|
BaseHooks []BaseHook
|
|
AuxiliaryHooks []AuxiliaryHook
|
|
Components []common.ApplicationComponent
|
|
|
|
AppLabels map[string]string
|
|
AppAnnotations map[string]string
|
|
}
|
|
|
|
// NewContext create render templateContext
|
|
func NewContext(data ContextData) Context {
|
|
ctx := &templateContext{
|
|
namespace: data.Namespace,
|
|
name: data.CompName,
|
|
appName: data.AppName,
|
|
appRevision: data.AppRevisionName,
|
|
workflowName: data.WorkflowName,
|
|
publishVersion: data.PublishVersion,
|
|
|
|
configs: []map[string]string{},
|
|
auxiliaries: []Auxiliary{},
|
|
parameters: map[string]interface{}{},
|
|
|
|
ctx: data.Ctx,
|
|
baseHooks: data.BaseHooks,
|
|
auxiliaryHooks: data.AuxiliaryHooks,
|
|
components: data.Components,
|
|
appLabels: data.AppLabels,
|
|
appAnnotations: data.AppAnnotations,
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
// SetParameters sets templateContext parameters
|
|
func (ctx *templateContext) SetParameters(params map[string]interface{}) {
|
|
ctx.parameters = params
|
|
}
|
|
|
|
// SetBase set templateContext base model
|
|
func (ctx *templateContext) SetBase(base model.Instance) error {
|
|
for _, hook := range ctx.baseHooks {
|
|
if err := hook.Exec(ctx, base); err != nil {
|
|
return errors.Wrap(err, "cannot set base into context")
|
|
}
|
|
}
|
|
ctx.base = base
|
|
return nil
|
|
}
|
|
|
|
// AppendAuxiliaries add Assist model to templateContext
|
|
func (ctx *templateContext) AppendAuxiliaries(auxiliaries ...Auxiliary) error {
|
|
for _, hook := range ctx.auxiliaryHooks {
|
|
if err := hook.Exec(ctx, auxiliaries); err != nil {
|
|
return errors.Wrap(err, "cannot append auxiliaries into context")
|
|
}
|
|
}
|
|
ctx.auxiliaries = append(ctx.auxiliaries, auxiliaries...)
|
|
return nil
|
|
}
|
|
|
|
// BaseContextFile return cue format string of templateContext
|
|
func (ctx *templateContext) BaseContextFile() string {
|
|
var buff string
|
|
buff += fmt.Sprintf(model.ContextName+": \"%s\"\n", ctx.name)
|
|
buff += fmt.Sprintf(model.ContextAppName+": \"%s\"\n", ctx.appName)
|
|
buff += fmt.Sprintf(model.ContextAppRevision+": \"%s\"\n", ctx.appRevision)
|
|
// the appRevision is generated by vela, the error always is nil, so ignore it
|
|
revNum, _ := util.ExtractRevisionNum(ctx.appRevision, "-")
|
|
buff += fmt.Sprintf(model.ContextAppRevisionNum+": %d\n", revNum)
|
|
buff += fmt.Sprintf(model.ContextNamespace+": \"%s\"\n", ctx.namespace)
|
|
buff += fmt.Sprintf(model.ContextCompRevisionName+": \"%s\"\n", model.ComponentRevisionPlaceHolder)
|
|
buff += fmt.Sprintf(model.ContextWorkflowName+": \"%s\"\n", ctx.workflowName)
|
|
buff += fmt.Sprintf(model.ContextPublishVersion+": \"%s\"\n", ctx.publishVersion)
|
|
|
|
if ctx.appLabels != nil {
|
|
bt, _ := json.Marshal(ctx.appLabels)
|
|
buff += model.ContextAppLabels + ": " + string(bt) + "\n"
|
|
}
|
|
|
|
if ctx.appAnnotations != nil {
|
|
bt, _ := json.Marshal(ctx.appAnnotations)
|
|
buff += model.ContextAppAnnotations + ": " + string(bt) + "\n"
|
|
}
|
|
|
|
if ctx.base != nil {
|
|
buff += fmt.Sprintf(model.OutputFieldName+": %s\n", structMarshal(ctx.base.String()))
|
|
}
|
|
|
|
if ctx.components != nil {
|
|
bt, _ := json.Marshal(ctx.components)
|
|
buff += fmt.Sprintf(model.ContextComponents+":%s\n", string(bt))
|
|
}
|
|
|
|
if len(ctx.auxiliaries) > 0 {
|
|
var auxLines []string
|
|
for _, auxiliary := range ctx.auxiliaries {
|
|
auxLines = append(auxLines, fmt.Sprintf("\"%s\": %s", auxiliary.Name, structMarshal(auxiliary.Ins.String())))
|
|
}
|
|
if len(auxLines) > 0 {
|
|
buff += fmt.Sprintf(model.OutputsFieldName+": {%s}\n", strings.Join(auxLines, "\n"))
|
|
}
|
|
}
|
|
|
|
if len(ctx.configs) > 0 {
|
|
bt, _ := json.Marshal(ctx.configs)
|
|
buff += model.ConfigFieldName + ": " + string(bt) + "\n"
|
|
}
|
|
|
|
if len(ctx.requiredSecrets) > 0 {
|
|
for _, s := range ctx.requiredSecrets {
|
|
data, _ := json.Marshal(s.Data)
|
|
buff += s.ContextName + ":" + string(data) + "\n"
|
|
}
|
|
}
|
|
|
|
if ctx.parameters != nil {
|
|
bt, _ := json.Marshal(ctx.parameters)
|
|
buff += model.ParameterFieldName + ": " + string(bt) + "\n"
|
|
}
|
|
|
|
if ctx.outputSecretName != "" {
|
|
buff += fmt.Sprintf("%s:\"%s\"", model.OutputSecretName, ctx.outputSecretName)
|
|
}
|
|
|
|
if ctx.data != nil {
|
|
d, _ := json.Marshal(ctx.data)
|
|
buff += fmt.Sprintf("\n %s", structMarshal(string(d)))
|
|
}
|
|
|
|
return fmt.Sprintf("context: %s", structMarshal(buff))
|
|
}
|
|
|
|
// ExtendedContextFile return cue format string of templateContext and extended secret context
|
|
func (ctx *templateContext) ExtendedContextFile() string {
|
|
context := ctx.BaseContextFile()
|
|
|
|
var bareSecret string
|
|
if len(ctx.requiredSecrets) > 0 {
|
|
for _, s := range ctx.requiredSecrets {
|
|
data, _ := json.Marshal(s.Data)
|
|
bareSecret += s.ContextName + ":" + string(data) + "\n"
|
|
}
|
|
}
|
|
if bareSecret != "" {
|
|
return context + "\n" + bareSecret
|
|
}
|
|
return context
|
|
}
|
|
|
|
func (ctx *templateContext) BaseContextLabels() map[string]string {
|
|
return map[string]string{
|
|
// appName is oam.LabelAppName
|
|
model.ContextAppName: ctx.appName,
|
|
// name is oam.LabelAppComponent
|
|
model.ContextName: ctx.name,
|
|
// appRevision is oam.LabelAppRevision
|
|
model.ContextAppRevision: ctx.appRevision,
|
|
}
|
|
}
|
|
|
|
// Output return model and auxiliaries of templateContext
|
|
func (ctx *templateContext) Output() (model.Instance, []Auxiliary) {
|
|
return ctx.base, ctx.auxiliaries
|
|
}
|
|
|
|
// InsertSecrets will add cloud resource secret stuff to context
|
|
func (ctx *templateContext) InsertSecrets(outputSecretName string, requiredSecrets []RequiredSecrets) {
|
|
if outputSecretName != "" {
|
|
ctx.outputSecretName = outputSecretName
|
|
}
|
|
if requiredSecrets != nil {
|
|
ctx.requiredSecrets = requiredSecrets
|
|
}
|
|
}
|
|
|
|
// PushData appends arbitrary extension data to context
|
|
func (ctx *templateContext) PushData(key string, data interface{}) {
|
|
if ctx.data == nil {
|
|
ctx.data = map[string]interface{}{key: data}
|
|
return
|
|
}
|
|
ctx.data[key] = data
|
|
}
|
|
|
|
func (ctx *templateContext) GetCtx() context.Context {
|
|
if ctx.ctx != nil {
|
|
return ctx.ctx
|
|
}
|
|
return context.TODO()
|
|
}
|
|
|
|
func (ctx *templateContext) SetCtx(newContext context.Context) {
|
|
if ctx.ctx != nil {
|
|
return
|
|
}
|
|
ctx.ctx = newContext
|
|
}
|
|
|
|
func structMarshal(v string) string {
|
|
skip := false
|
|
v = strings.TrimFunc(v, func(r rune) bool {
|
|
if !skip {
|
|
if unicode.IsSpace(r) {
|
|
return true
|
|
}
|
|
skip = true
|
|
|
|
}
|
|
return false
|
|
})
|
|
|
|
if strings.HasPrefix(v, "{") {
|
|
return v
|
|
}
|
|
return fmt.Sprintf("{%s}", v)
|
|
}
|