2021-03-26 15:24:19 +08:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
package appfile
|
|
|
|
|
|
|
|
import (
|
2021-03-05 15:14:18 +08:00
|
|
|
"context"
|
2021-03-15 15:54:43 +08:00
|
|
|
"fmt"
|
2021-03-05 15:14:18 +08:00
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
2021-09-12 10:12:46 +08:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2021-05-30 22:57:03 +08:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
2021-01-28 18:36:52 +08:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
|
2021-07-30 10:02:51 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
2021-12-10 15:00:03 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
2021-03-25 08:15:20 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
2021-01-28 18:36:52 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
2021-06-02 15:37:06 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/cue/definition"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
2022-01-14 15:18:02 +08:00
|
|
|
monitorContext "github.com/oam-dev/kubevela/pkg/monitor/context"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
|
2021-04-13 12:29:25 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam"
|
2021-01-28 18:36:52 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
2022-03-07 10:21:00 +08:00
|
|
|
policypkg "github.com/oam-dev/kubevela/pkg/policy"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/workflow/step"
|
2021-01-28 18:36:52 +08:00
|
|
|
)
|
|
|
|
|
2021-04-13 12:29:25 +08:00
|
|
|
// TemplateLoaderFn load template of a capability definition
|
|
|
|
type TemplateLoaderFn func(context.Context, discoverymapper.DiscoveryMapper, client.Reader, string, types.CapType) (*Template, error)
|
|
|
|
|
|
|
|
// LoadTemplate load template of a capability definition
|
|
|
|
func (fn TemplateLoaderFn) LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, c client.Reader, capName string, capType types.CapType) (*Template, error) {
|
|
|
|
return fn(ctx, dm, c, capName, capType)
|
|
|
|
}
|
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
// Parser is an application parser
|
|
|
|
type Parser struct {
|
2021-04-13 12:29:25 +08:00
|
|
|
client client.Client
|
|
|
|
dm discoverymapper.DiscoveryMapper
|
2021-06-02 15:37:06 +08:00
|
|
|
pd *packages.PackageDiscover
|
2021-04-13 12:29:25 +08:00
|
|
|
tmplLoader TemplateLoaderFn
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewApplicationParser create appfile parser
|
2021-06-02 15:37:06 +08:00
|
|
|
func NewApplicationParser(cli client.Client, dm discoverymapper.DiscoveryMapper, pd *packages.PackageDiscover) *Parser {
|
2021-01-28 18:36:52 +08:00
|
|
|
return &Parser{
|
2021-04-13 12:29:25 +08:00
|
|
|
client: cli,
|
|
|
|
dm: dm,
|
|
|
|
pd: pd,
|
|
|
|
tmplLoader: LoadTemplate,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDryRunApplicationParser create an appfile parser for DryRun
|
2021-06-02 15:37:06 +08:00
|
|
|
func NewDryRunApplicationParser(cli client.Client, dm discoverymapper.DiscoveryMapper, pd *packages.PackageDiscover, defs []oam.Object) *Parser {
|
2021-04-13 12:29:25 +08:00
|
|
|
return &Parser{
|
|
|
|
client: cli,
|
|
|
|
dm: dm,
|
|
|
|
pd: pd,
|
|
|
|
tmplLoader: DryRunTemplateLoader(defs),
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateAppFile converts an application to an Appfile
|
2021-04-11 14:10:16 +08:00
|
|
|
func (p *Parser) GenerateAppFile(ctx context.Context, app *v1beta1.Application) (*Appfile, error) {
|
2022-01-14 15:18:02 +08:00
|
|
|
if ctx, ok := ctx.(monitorContext.Context); ok {
|
|
|
|
subCtx := ctx.Fork("generate-app-file", monitorContext.DurationMetric(func(v float64) {
|
|
|
|
metrics.ParseAppFileDurationHistogram.WithLabelValues("application").Observe(v)
|
|
|
|
}))
|
|
|
|
defer subCtx.Commit("finish generate appFile")
|
|
|
|
}
|
2021-04-11 14:10:16 +08:00
|
|
|
ns := app.Namespace
|
|
|
|
appName := app.Name
|
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
appfile := p.newAppfile(appName, ns, app)
|
2021-11-26 17:47:43 +08:00
|
|
|
if app.Status.LatestRevision != nil {
|
|
|
|
appfile.AppRevisionName = app.Status.LatestRevision.Name
|
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
var wds []*Workload
|
|
|
|
for _, comp := range app.Spec.Components {
|
2021-07-02 15:09:45 +08:00
|
|
|
wd, err := p.parseWorkload(ctx, comp)
|
2021-01-28 18:36:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-07-02 15:09:45 +08:00
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
wds = append(wds, wd)
|
|
|
|
}
|
|
|
|
appfile.Workloads = wds
|
2021-08-04 18:58:57 +08:00
|
|
|
appfile.Components = app.Spec.Components
|
2021-06-15 13:40:12 +08:00
|
|
|
|
|
|
|
var err error
|
2022-03-07 10:21:00 +08:00
|
|
|
// parse workflow steps
|
|
|
|
appfile.WorkflowMode = common.WorkflowModeDAG
|
|
|
|
if wfSpec := app.Spec.Workflow; wfSpec != nil && len(wfSpec.Steps) > 0 {
|
|
|
|
appfile.WorkflowMode = common.WorkflowModeStep
|
|
|
|
appfile.WorkflowSteps = wfSpec.Steps
|
|
|
|
}
|
|
|
|
appfile.WorkflowSteps, err = step.NewChainWorkflowStepGenerator(
|
|
|
|
&step.RefWorkflowStepGenerator{Client: p.client, Context: ctx},
|
|
|
|
&step.DeployWorkflowStepGenerator{},
|
|
|
|
&step.Deploy2EnvWorkflowStepGenerator{},
|
|
|
|
&step.ApplyComponentWorkflowStepGenerator{},
|
|
|
|
&step.DeployPreApproveWorkflowStepGenerator{},
|
|
|
|
).Generate(appfile.app, appfile.WorkflowSteps)
|
2021-06-15 13:40:12 +08:00
|
|
|
if err != nil {
|
2022-03-07 10:21:00 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = p.parsePolicies(ctx, appfile); err != nil {
|
2021-06-15 13:40:12 +08:00
|
|
|
return nil, fmt.Errorf("failed to parsePolicies: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
for _, w := range wds {
|
|
|
|
if w == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if w.FullTemplate.ComponentDefinition != nil {
|
|
|
|
cd := w.FullTemplate.ComponentDefinition.DeepCopy()
|
|
|
|
cd.Status = v1beta1.ComponentDefinitionStatus{}
|
|
|
|
appfile.RelatedComponentDefinitions[w.FullTemplate.ComponentDefinition.Name] = cd
|
|
|
|
}
|
|
|
|
for _, t := range w.Traits {
|
|
|
|
if t == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if t.FullTemplate.TraitDefinition != nil {
|
|
|
|
td := t.FullTemplate.TraitDefinition.DeepCopy()
|
|
|
|
td.Status = v1beta1.TraitDefinitionStatus{}
|
|
|
|
appfile.RelatedTraitDefinitions[t.FullTemplate.TraitDefinition.Name] = td
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, s := range w.ScopeDefinition {
|
|
|
|
if s == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
appfile.RelatedScopeDefinitions[s.Name] = s.DeepCopy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return appfile, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) newAppfile(appName, ns string, app *v1beta1.Application) *Appfile {
|
|
|
|
file := &Appfile{
|
|
|
|
Name: appName,
|
|
|
|
Namespace: ns,
|
|
|
|
|
|
|
|
AppLabels: make(map[string]string),
|
|
|
|
AppAnnotations: make(map[string]string),
|
|
|
|
RelatedTraitDefinitions: make(map[string]*v1beta1.TraitDefinition),
|
|
|
|
RelatedComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition),
|
|
|
|
RelatedScopeDefinitions: make(map[string]*v1beta1.ScopeDefinition),
|
|
|
|
|
|
|
|
parser: p,
|
2021-11-08 23:11:50 +08:00
|
|
|
app: app,
|
2021-09-12 10:12:46 +08:00
|
|
|
}
|
|
|
|
for k, v := range app.Annotations {
|
|
|
|
file.AppAnnotations[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range app.Labels {
|
|
|
|
file.AppLabels[k] = v
|
|
|
|
}
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
|
|
|
// inheritLabelAndAnnotationFromAppRev is a compatible function, that we can't record metadata for application object in AppRev
|
|
|
|
func inheritLabelAndAnnotationFromAppRev(appRev *v1beta1.ApplicationRevision) {
|
|
|
|
if len(appRev.Spec.Application.Annotations) > 0 || len(appRev.Spec.Application.Labels) > 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
appRev.Spec.Application.SetNamespace(appRev.Namespace)
|
|
|
|
if appRev.Spec.Application.GetName() == "" {
|
|
|
|
appRev.Spec.Application.SetName(appRev.Labels[oam.LabelAppName])
|
|
|
|
}
|
|
|
|
labels := make(map[string]string)
|
|
|
|
for k, v := range appRev.GetLabels() {
|
|
|
|
if k == oam.LabelAppRevisionHash || k == oam.LabelAppName {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
labels[k] = v
|
|
|
|
}
|
|
|
|
appRev.Spec.Application.SetLabels(labels)
|
|
|
|
|
|
|
|
annotations := make(map[string]string)
|
|
|
|
for k, v := range appRev.GetAnnotations() {
|
|
|
|
annotations[k] = v
|
|
|
|
}
|
|
|
|
appRev.Spec.Application.SetAnnotations(annotations)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateAppFileFromRevision converts an application revision to an Appfile
|
|
|
|
func (p *Parser) GenerateAppFileFromRevision(appRev *v1beta1.ApplicationRevision) (*Appfile, error) {
|
|
|
|
|
|
|
|
inheritLabelAndAnnotationFromAppRev(appRev)
|
|
|
|
|
|
|
|
app := appRev.Spec.Application.DeepCopy()
|
|
|
|
ns := app.Namespace
|
|
|
|
appName := app.Name
|
|
|
|
appfile := p.newAppfile(appName, ns, app)
|
|
|
|
appfile.AppRevisionName = appRev.Name
|
|
|
|
appfile.AppRevisionHash = appRev.Labels[oam.LabelAppRevisionHash]
|
|
|
|
|
|
|
|
var wds []*Workload
|
|
|
|
for _, comp := range app.Spec.Components {
|
|
|
|
wd, err := p.ParseWorkloadFromRevision(comp, appRev)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
wds = append(wds, wd)
|
|
|
|
}
|
|
|
|
appfile.Workloads = wds
|
|
|
|
appfile.Components = app.Spec.Components
|
|
|
|
|
|
|
|
var err error
|
2022-03-07 10:21:00 +08:00
|
|
|
appfile.PolicyWorkloads, err = p.parsePoliciesFromRevision(app.Spec.Policies, appRev)
|
2021-09-12 10:12:46 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parsePolicies: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range appRev.Spec.ComponentDefinitions {
|
|
|
|
appfile.RelatedComponentDefinitions[k] = v.DeepCopy()
|
|
|
|
}
|
|
|
|
for k, v := range appRev.Spec.TraitDefinitions {
|
|
|
|
appfile.RelatedTraitDefinitions[k] = v.DeepCopy()
|
|
|
|
}
|
|
|
|
for k, v := range appRev.Spec.ScopeDefinitions {
|
|
|
|
appfile.RelatedScopeDefinitions[k] = v.DeepCopy()
|
|
|
|
}
|
|
|
|
|
2021-07-22 18:53:30 +08:00
|
|
|
if wfSpec := app.Spec.Workflow; wfSpec != nil {
|
|
|
|
appfile.WorkflowSteps = wfSpec.Steps
|
2021-06-15 13:40:12 +08:00
|
|
|
}
|
2021-07-22 18:53:30 +08:00
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
return appfile, nil
|
|
|
|
}
|
|
|
|
|
2022-03-07 10:21:00 +08:00
|
|
|
func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) error {
|
2021-06-15 13:40:12 +08:00
|
|
|
ws := []*Workload{}
|
2022-03-07 10:21:00 +08:00
|
|
|
|
|
|
|
policies, err := step.LoadExternalPoliciesForWorkflow(ctx, p.client, af.app.GetNamespace(), af.WorkflowSteps, af.app.Spec.Policies)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var compDefs []*v1beta1.ComponentDefinition
|
|
|
|
var traitDefs []*v1beta1.TraitDefinition
|
2021-06-15 13:40:12 +08:00
|
|
|
for _, policy := range policies {
|
2021-11-24 15:38:13 +08:00
|
|
|
var w *Workload
|
|
|
|
var err error
|
2022-03-07 10:21:00 +08:00
|
|
|
var _compDefs []*v1beta1.ComponentDefinition
|
|
|
|
var _traitDefs []*v1beta1.TraitDefinition
|
2021-12-10 15:00:03 +08:00
|
|
|
switch policy.Type {
|
|
|
|
case v1alpha1.GarbageCollectPolicyType:
|
2021-11-24 15:38:13 +08:00
|
|
|
w, err = p.makeBuiltInPolicy(policy.Name, policy.Type, policy.Properties)
|
2021-12-10 15:00:03 +08:00
|
|
|
case v1alpha1.ApplyOncePolicyType:
|
|
|
|
w, err = p.makeBuiltInPolicy(policy.Name, policy.Type, policy.Properties)
|
2022-03-07 10:21:00 +08:00
|
|
|
case v1alpha1.TopologyPolicyType:
|
|
|
|
w, err = p.makeBuiltInPolicy(policy.Name, policy.Type, policy.Properties)
|
|
|
|
case v1alpha1.OverridePolicyType:
|
|
|
|
w, err = p.makeBuiltInPolicy(policy.Name, policy.Type, policy.Properties)
|
|
|
|
if err == nil {
|
|
|
|
_compDefs, _traitDefs, err = policypkg.ParseOverridePolicyRelatedDefinitions(ctx, p.client, af.app, policy)
|
|
|
|
}
|
2021-12-10 15:00:03 +08:00
|
|
|
default:
|
2021-11-24 15:38:13 +08:00
|
|
|
w, err = p.makeWorkload(ctx, policy.Name, policy.Type, types.TypePolicy, policy.Properties)
|
|
|
|
}
|
2021-06-15 13:40:12 +08:00
|
|
|
if err != nil {
|
2022-03-07 10:21:00 +08:00
|
|
|
return err
|
2021-06-15 13:40:12 +08:00
|
|
|
}
|
|
|
|
ws = append(ws, w)
|
2022-03-07 10:21:00 +08:00
|
|
|
compDefs = append(compDefs, _compDefs...)
|
|
|
|
traitDefs = append(traitDefs, _traitDefs...)
|
2021-06-15 13:40:12 +08:00
|
|
|
}
|
2022-03-07 10:21:00 +08:00
|
|
|
|
|
|
|
for _, def := range compDefs {
|
|
|
|
af.RelatedComponentDefinitions[def.Name] = def
|
|
|
|
}
|
|
|
|
for _, def := range traitDefs {
|
|
|
|
af.RelatedTraitDefinitions[def.Name] = def
|
|
|
|
}
|
|
|
|
af.Policies = policies
|
|
|
|
af.PolicyWorkloads = ws
|
|
|
|
return nil
|
2021-06-15 13:40:12 +08:00
|
|
|
}
|
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
func (p *Parser) parsePoliciesFromRevision(policies []v1beta1.AppPolicy, appRev *v1beta1.ApplicationRevision) ([]*Workload, error) {
|
|
|
|
ws := []*Workload{}
|
|
|
|
for _, policy := range policies {
|
|
|
|
w, err := p.makeWorkloadFromRevision(policy.Name, policy.Type, types.TypePolicy, policy.Properties, appRev)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ws = append(ws, w)
|
|
|
|
}
|
|
|
|
return ws, nil
|
|
|
|
}
|
|
|
|
|
2021-10-13 16:16:53 +08:00
|
|
|
func (p *Parser) makeWorkload(ctx context.Context, name, typ string, capType types.CapType, props *runtime.RawExtension) (*Workload, error) {
|
2021-05-30 22:57:03 +08:00
|
|
|
templ, err := p.tmplLoader.LoadTemplate(ctx, p.dm, p.client, typ, capType)
|
2021-07-31 07:11:07 +08:00
|
|
|
if err != nil {
|
2021-09-12 10:12:46 +08:00
|
|
|
return nil, errors.WithMessagef(err, "fetch component/policy type of %s", name)
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
return p.convertTemplate2Workload(name, typ, props, templ)
|
|
|
|
}
|
|
|
|
|
2021-11-24 15:38:13 +08:00
|
|
|
func (p *Parser) makeBuiltInPolicy(name, typ string, props *runtime.RawExtension) (*Workload, error) {
|
|
|
|
settings, err := util.RawExtension2Map(props)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessagef(err, "fail to parse settings for %s", name)
|
|
|
|
}
|
|
|
|
return &Workload{
|
|
|
|
Traits: []*Trait{},
|
|
|
|
ScopeDefinition: []*v1beta1.ScopeDefinition{},
|
|
|
|
Name: name,
|
|
|
|
Type: typ,
|
|
|
|
Params: settings,
|
|
|
|
engine: definition.NewWorkloadAbstractEngine(name, p.pd),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-10-13 16:16:53 +08:00
|
|
|
func (p *Parser) makeWorkloadFromRevision(name, typ string, capType types.CapType, props *runtime.RawExtension, appRev *v1beta1.ApplicationRevision) (*Workload, error) {
|
2021-09-13 14:17:12 +08:00
|
|
|
templ, err := LoadTemplateFromRevision(typ, capType, appRev, p.dm)
|
2021-09-12 10:12:46 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessagef(err, "fetch component/policy type of %s from revision", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return p.convertTemplate2Workload(name, typ, props, templ)
|
|
|
|
}
|
|
|
|
|
2021-10-13 16:16:53 +08:00
|
|
|
func (p *Parser) convertTemplate2Workload(name, typ string, props *runtime.RawExtension, templ *Template) (*Workload, error) {
|
|
|
|
settings, err := util.RawExtension2Map(props)
|
2021-01-28 18:36:52 +08:00
|
|
|
if err != nil {
|
2021-05-30 22:57:03 +08:00
|
|
|
return nil, errors.WithMessagef(err, "fail to parse settings for %s", name)
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
2021-05-30 22:57:03 +08:00
|
|
|
wlType, err := util.ConvertDefinitionRevName(typ)
|
2021-05-07 17:52:44 +08:00
|
|
|
if err != nil {
|
2021-05-30 22:57:03 +08:00
|
|
|
wlType = typ
|
2021-05-07 17:52:44 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
return &Workload{
|
2021-04-02 10:24:27 +08:00
|
|
|
Traits: []*Trait{},
|
2021-09-12 10:12:46 +08:00
|
|
|
ScopeDefinition: []*v1beta1.ScopeDefinition{},
|
2021-05-30 22:57:03 +08:00
|
|
|
Name: name,
|
2021-05-07 17:52:44 +08:00
|
|
|
Type: wlType,
|
2021-04-02 10:24:27 +08:00
|
|
|
CapabilityCategory: templ.CapabilityCategory,
|
|
|
|
FullTemplate: templ,
|
|
|
|
Params: settings,
|
2021-05-30 22:57:03 +08:00
|
|
|
engine: definition.NewWorkloadAbstractEngine(name, p.pd),
|
2021-09-12 10:12:46 +08:00
|
|
|
}, nil
|
2021-05-30 22:57:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// parseWorkload resolve an ApplicationComponent and generate a Workload
|
|
|
|
// containing ALL information required by an Appfile.
|
2021-07-30 10:02:51 +08:00
|
|
|
func (p *Parser) parseWorkload(ctx context.Context, comp common.ApplicationComponent) (*Workload, error) {
|
2021-07-02 15:09:45 +08:00
|
|
|
workload, err := p.makeWorkload(ctx, comp.Name, comp.Type, types.TypeComponentDefinition, comp.Properties)
|
2021-05-30 22:57:03 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-08-08 12:11:24 +08:00
|
|
|
workload.ExternalRevision = comp.ExternalRevision
|
2021-04-11 14:10:16 +08:00
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
for _, traitValue := range comp.Traits {
|
2021-10-13 16:16:53 +08:00
|
|
|
properties, err := util.RawExtension2Map(traitValue.Properties)
|
2021-01-28 18:36:52 +08:00
|
|
|
if err != nil {
|
2021-03-25 08:15:20 +08:00
|
|
|
return nil, errors.Errorf("fail to parse properties of %s for %s", traitValue.Type, comp.Name)
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
2021-03-25 08:15:20 +08:00
|
|
|
trait, err := p.parseTrait(ctx, traitValue.Type, properties)
|
2021-01-28 18:36:52 +08:00
|
|
|
if err != nil {
|
2021-03-25 08:15:20 +08:00
|
|
|
return nil, errors.WithMessagef(err, "component(%s) parse trait(%s)", comp.Name, traitValue.Type)
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
workload.Traits = append(workload.Traits, trait)
|
|
|
|
}
|
|
|
|
for scopeType, instanceName := range comp.Scopes {
|
2021-09-12 10:12:46 +08:00
|
|
|
sd, gvk, err := GetScopeDefAndGVK(ctx, p.client, p.dm, scopeType)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
workload.Scopes = append(workload.Scopes, Scope{
|
|
|
|
Name: instanceName,
|
|
|
|
GVK: gvk,
|
|
|
|
ResourceVersion: sd.Spec.Reference.Name + "/" + sd.Spec.Reference.Version,
|
|
|
|
})
|
|
|
|
workload.ScopeDefinition = append(workload.ScopeDefinition, sd)
|
|
|
|
}
|
|
|
|
return workload, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseWorkloadFromRevision resolve an ApplicationComponent and generate a Workload
|
|
|
|
// containing ALL information required by an Appfile from app revision.
|
|
|
|
func (p *Parser) ParseWorkloadFromRevision(comp common.ApplicationComponent, appRev *v1beta1.ApplicationRevision) (*Workload, error) {
|
|
|
|
workload, err := p.makeWorkloadFromRevision(comp.Name, comp.Type, types.TypeComponentDefinition, comp.Properties, appRev)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
workload.ExternalRevision = comp.ExternalRevision
|
|
|
|
|
|
|
|
for _, traitValue := range comp.Traits {
|
2021-10-13 16:16:53 +08:00
|
|
|
properties, err := util.RawExtension2Map(traitValue.Properties)
|
2021-09-12 10:12:46 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Errorf("fail to parse properties of %s for %s", traitValue.Type, comp.Name)
|
|
|
|
}
|
|
|
|
trait, err := p.parseTraitFromRevision(traitValue.Type, properties, appRev)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessagef(err, "component(%s) parse trait(%s)", comp.Name, traitValue.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
workload.Traits = append(workload.Traits, trait)
|
|
|
|
}
|
|
|
|
for scopeType, instanceName := range comp.Scopes {
|
|
|
|
sd, gvk, err := GetScopeDefAndGVKFromRevision(scopeType, appRev)
|
2021-01-28 18:36:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
workload.Scopes = append(workload.Scopes, Scope{
|
2021-09-12 10:12:46 +08:00
|
|
|
Name: instanceName,
|
|
|
|
GVK: gvk,
|
|
|
|
ResourceVersion: sd.Spec.Reference.Name + "/" + sd.Spec.Reference.Version,
|
2021-01-28 18:36:52 +08:00
|
|
|
})
|
2021-09-12 10:12:46 +08:00
|
|
|
workload.ScopeDefinition = append(workload.ScopeDefinition, sd)
|
2021-01-28 18:36:52 +08:00
|
|
|
}
|
|
|
|
return workload, nil
|
|
|
|
}
|
2021-06-15 13:40:12 +08:00
|
|
|
|
2021-03-05 15:14:18 +08:00
|
|
|
func (p *Parser) parseTrait(ctx context.Context, name string, properties map[string]interface{}) (*Trait, error) {
|
2021-04-13 12:29:25 +08:00
|
|
|
templ, err := p.tmplLoader.LoadTemplate(ctx, p.dm, p.client, name, types.TypeTrait)
|
2021-01-28 18:36:52 +08:00
|
|
|
if kerrors.IsNotFound(err) {
|
|
|
|
return nil, errors.Errorf("trait definition of %s not found", name)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
return p.convertTemplate2Trait(name, properties, templ)
|
|
|
|
}
|
2021-06-29 15:53:57 +08:00
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
func (p *Parser) parseTraitFromRevision(name string, properties map[string]interface{}, appRev *v1beta1.ApplicationRevision) (*Trait, error) {
|
2021-09-13 14:17:12 +08:00
|
|
|
templ, err := LoadTemplateFromRevision(name, types.TypeTrait, appRev, p.dm)
|
2021-09-12 10:12:46 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return p.convertTemplate2Trait(name, properties, templ)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) convertTemplate2Trait(name string, properties map[string]interface{}, templ *Template) (*Trait, error) {
|
2021-06-29 15:53:57 +08:00
|
|
|
traitName, err := util.ConvertDefinitionRevName(name)
|
|
|
|
if err != nil {
|
|
|
|
traitName = name
|
|
|
|
}
|
2021-01-28 18:36:52 +08:00
|
|
|
return &Trait{
|
2021-06-29 15:53:57 +08:00
|
|
|
Name: traitName,
|
2021-01-28 18:36:52 +08:00
|
|
|
CapabilityCategory: templ.CapabilityCategory,
|
|
|
|
Params: properties,
|
|
|
|
Template: templ.TemplateStr,
|
2021-02-01 10:43:44 +08:00
|
|
|
HealthCheckPolicy: templ.Health,
|
|
|
|
CustomStatusFormat: templ.CustomStatus,
|
2021-03-20 03:30:31 +08:00
|
|
|
FullTemplate: templ,
|
2021-06-29 15:53:57 +08:00
|
|
|
engine: definition.NewTraitAbstractEngine(traitName, p.pd),
|
2021-01-28 18:36:52 +08:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:47:17 +08:00
|
|
|
// ValidateComponentNames validate all component name whether repeat in cluster and template
|
|
|
|
func (p *Parser) ValidateComponentNames(ctx context.Context, af *Appfile) (int, error) {
|
|
|
|
existCompNames := make(map[string]string)
|
|
|
|
existApps := v1beta1.ApplicationList{}
|
2021-09-02 14:09:45 +08:00
|
|
|
|
|
|
|
listOpts := []client.ListOption{
|
|
|
|
client.InNamespace(af.Namespace),
|
|
|
|
}
|
|
|
|
if err := p.client.List(ctx, &existApps, listOpts...); err != nil {
|
2021-06-24 16:47:17 +08:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
for _, existApp := range existApps.Items {
|
|
|
|
ea := existApp.DeepCopy()
|
|
|
|
existAf, err := p.GenerateAppFile(ctx, ea)
|
|
|
|
if err != nil || existAf.Name == af.Name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, existComp := range existAf.Workloads {
|
|
|
|
existCompNames[existComp.Name] = existApp.Name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, wl := range af.Workloads {
|
|
|
|
if existAfName, ok := existCompNames[wl.Name]; ok {
|
|
|
|
return i, fmt.Errorf("component named '%s' is already exist in application '%s'", wl.Name, existAfName)
|
|
|
|
}
|
|
|
|
for j := i + 1; j < len(af.Workloads); j++ {
|
|
|
|
if wl.Name == af.Workloads[j].Name {
|
|
|
|
return i, fmt.Errorf("component named '%s' is repeat in this appfile", wl.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
// GetScopeDefAndGVK get grouped API version of the given scope
|
|
|
|
func GetScopeDefAndGVK(ctx context.Context, cli client.Reader, dm discoverymapper.DiscoveryMapper,
|
|
|
|
name string) (*v1beta1.ScopeDefinition, metav1.GroupVersionKind, error) {
|
|
|
|
var gvk metav1.GroupVersionKind
|
|
|
|
sd := new(v1beta1.ScopeDefinition)
|
|
|
|
err := util.GetDefinition(ctx, cli, sd, name)
|
2021-03-29 17:20:33 +08:00
|
|
|
if err != nil {
|
2021-09-12 10:12:46 +08:00
|
|
|
return nil, gvk, err
|
2021-03-29 17:20:33 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
gvk, err = util.GetGVKFromDefinition(dm, sd.Spec.Reference)
|
2021-04-11 14:10:16 +08:00
|
|
|
if err != nil {
|
2021-09-12 10:12:46 +08:00
|
|
|
return nil, gvk, err
|
2021-03-29 17:20:33 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
return sd, gvk, nil
|
2021-03-29 17:20:33 +08:00
|
|
|
}
|
|
|
|
|
2021-09-12 10:12:46 +08:00
|
|
|
// GetScopeDefAndGVKFromRevision get grouped API version of the given scope
|
|
|
|
func GetScopeDefAndGVKFromRevision(name string, appRev *v1beta1.ApplicationRevision) (*v1beta1.ScopeDefinition, metav1.GroupVersionKind, error) {
|
|
|
|
var gvk metav1.GroupVersionKind
|
|
|
|
sd, ok := appRev.Spec.ScopeDefinitions[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, gvk, fmt.Errorf("scope %s not found in application revision", name)
|
2021-03-29 17:20:33 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
gvk, ok = appRev.Spec.ScopeGVK[sd.Spec.Reference.Name+"/"+sd.Spec.Reference.Version]
|
|
|
|
if !ok {
|
|
|
|
return nil, gvk, fmt.Errorf("scope definition found but GVK %s not found in application revision", name)
|
2021-04-13 12:29:25 +08:00
|
|
|
}
|
2021-09-12 10:12:46 +08:00
|
|
|
return sd.DeepCopy(), gvk, nil
|
2021-04-13 12:29:25 +08:00
|
|
|
}
|