Skip to content

Commit

Permalink
[Feature] vela dry-run render results should be affected by override …
Browse files Browse the repository at this point in the history
…policy and deploy workflowstep

Signed-off-by: cezhang <c1zhang.dev@gmail.com>
  • Loading branch information
cezhang committed Dec 16, 2022
1 parent 2b3da03 commit ac73bef
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 4 deletions.
118 changes: 118 additions & 0 deletions pkg/appfile/dryrun/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
"github.com/oam-dev/kubevela/pkg/utils"
"github.com/oam-dev/kubevela/pkg/workflow/step"
"os"
"path/filepath"

Expand Down Expand Up @@ -143,6 +147,7 @@ func (d *Option) ExecuteDryRun(ctx context.Context, application *v1beta1.Applica
if appFile.Namespace == "" {
appFile.Namespace = corev1.NamespaceDefault
}

comps, err := appFile.GenerateComponentManifests()
if err != nil {
return nil, nil, errors.WithMessage(err, "cannot generate manifests from components and traits")
Expand Down Expand Up @@ -206,3 +211,116 @@ func (d *Option) PrintDryRun(buff *bytes.Buffer, appName string, comps []*types.
}
return nil
}

func (d *Option) ExecuteDryRunWithPolicies(ctx context.Context, application *v1beta1.Application, buff *bytes.Buffer) error {

app := application.DeepCopy()
if app.Namespace == "" {
app.Namespace = corev1.NamespaceDefault
} else {
ctx = oamutil.SetNamespaceInCtx(ctx, app.Namespace)
}
parser := appfile.NewDryRunApplicationParser(d.Client, d.DiscoveryMapper, d.PackageDiscover, d.Auxiliaries)
af, err := parser.GenerateAppFileFromApp(ctx, app)
if err != nil {
return err
}

deployWorkflowCount := 0
for _, wfs := range af.WorkflowSteps {
if wfs.Type == step.DeployWorkflowStep {
deployWorkflowCount++
deployWorkflowStepSpec := &step.DeployWorkflowStepSpec{}
if err := utils.StrictUnmarshal(wfs.Properties.Raw, deployWorkflowStepSpec); err != nil {
return err
}

topologyPolicies, overridePolicies, err := filterPolicies(af.Policies, deployWorkflowStepSpec.Policies)
if err != nil {
return err
}
if len(topologyPolicies) > 0 {
for _, tp := range topologyPolicies {
patchedApp, err := patchApp(app, overridePolicies)
if err != nil {
return err
}
comps, pms, err := d.ExecuteDryRun(ctx, patchedApp)
if err != nil {
return err
}
err = d.PrintDryRun(buff, fmt.Sprintf("%s with topology %s", patchedApp.Name, tp.Name), comps, pms)
if err != nil {
return err
}
}
} else {
patchedApp, err := patchApp(app, overridePolicies)
comps, pms, err := d.ExecuteDryRun(ctx, patchedApp)
if err != nil {
return err
}
err = d.PrintDryRun(buff, fmt.Sprintf("%s only with override policies %s", patchedApp.Name), comps, pms)
if err != nil {
return err
}
}
}
}

if deployWorkflowCount == 0 {
comps, pms, err := d.ExecuteDryRun(ctx, app)
if err != nil {
return err
}
err = d.PrintDryRun(buff, app.Namespace, comps, pms)
if err != nil {
return err
}
}

return nil
}

func filterPolicies(policies []v1beta1.AppPolicy, policyNames []string) ([]v1beta1.AppPolicy, []v1beta1.AppPolicy, error) {
policyMap := make(map[string]v1beta1.AppPolicy)
for _, policy := range policies {
policyMap[policy.Name] = policy
}
var topologyPolicies []v1beta1.AppPolicy
var overridePolicies []v1beta1.AppPolicy
for _, policyName := range policyNames {
if policy, found := policyMap[policyName]; found {
switch policy.Type {
case v1alpha1.TopologyPolicyType:
topologyPolicies = append(topologyPolicies, policy)
case v1alpha1.OverridePolicyType:
overridePolicies = append(overridePolicies, policy)
}
} else {
return nil, nil, errors.Errorf("policy %s not found", policyName)
}
}
return topologyPolicies, overridePolicies, nil
}

func patchApp(application *v1beta1.Application, overridePolicies []v1beta1.AppPolicy) (*v1beta1.Application, error) {
app := application.DeepCopy()
for _, policy := range overridePolicies {

if policy.Properties == nil {
return nil, fmt.Errorf("override policy %s must not have empty properties", policy.Name)
}
overrideSpec := &v1alpha1.OverridePolicySpec{}
if err := utils.StrictUnmarshal(policy.Properties.Raw, overrideSpec); err != nil {
return nil, errors.Wrapf(err, "failed to parse override policy %s", policy.Name)
}
overrideComps, err := envbinding.PatchComponents(app.Spec.Components, overrideSpec.Components, overrideSpec.Selector)
if err != nil {
return nil, errors.Wrapf(err, "failed to apply override policy %s", policy.Name)
}
app.Spec.Components = overrideComps
}

return app, nil
}
48 changes: 44 additions & 4 deletions references/cli/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"context"
"encoding/json"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -47,6 +48,7 @@ type DryRunCmdOptions struct {
cmdutil.IOStreams
ApplicationFile string
DefinitionFile string
PolicyFile string
OfflineMode bool
}

Expand Down Expand Up @@ -91,6 +93,7 @@ You can also specify a remote url for app:
cmd.Flags().StringVarP(&o.ApplicationFile, "file", "f", "./app.yaml", "application file name")
cmd.Flags().StringVarP(&o.DefinitionFile, "definition", "d", "", "specify a definition file or directory, it will only be used in dry-run rather than applied to K8s cluster")
cmd.Flags().BoolVar(&o.OfflineMode, "offline", false, "Run `dry-run` in offline / local mode, all validation steps will be skipped")
cmd.Flags().StringVarP(&o.PolicyFile, "policy", "p", "", "policy file name")
addNamespaceAndEnvArg(cmd)
cmd.SetOut(ioStreams.Out)
return cmd
Expand Down Expand Up @@ -151,12 +154,22 @@ func DryRunApplication(cmdOption *DryRunCmdOptions, c common.Args, namespace str
return buff, errors.WithMessagef(err, "read application file: %s", cmdOption.ApplicationFile)
}

comps, policies, err := dryRunOpt.ExecuteDryRun(ctx, app)
if err != nil {
return buff, errors.WithMessage(err, "generate OAM objects")
if cmdOption.PolicyFile != "" {
policies, err := readPolicyFromFile(cmdOption.PolicyFile)
if err != nil {
return buff, errors.WithMessagef(err, "read policy file: %s", cmdOption.PolicyFile)
}
for _, policy := range policies {
app.Spec.Policies = append(app.Spec.Policies, corev1beta1.AppPolicy{
Name: policy.Name,
Type: policy.Type,
Properties: policy.Properties,
})
}
}

if err = dryRunOpt.PrintDryRun(&buff, app.Name, comps, policies); err != nil {
err = dryRunOpt.ExecuteDryRunWithPolicies(ctx, app, &buff)
if err != nil {
return buff, err
}
return buff, nil
Expand Down Expand Up @@ -241,3 +254,30 @@ func readApplicationFromFile(filename string) (*corev1beta1.Application, error)
err = json.Unmarshal(fileContent, app)
return app, err
}

func readPolicyFromFile(filename string) ([]*v1alpha1.Policy, error) {
var policies []*v1alpha1.Policy
fileContent, err := utils.ReadRemoteOrLocalPath(filename, true)
if err != nil {
return nil, err
}

fileType := filepath.Ext(filename)
switch fileType {
case ".yaml", ".yml":
yamlFileContents := bytes.Split(fileContent, []byte("---"))
for _, yamlFileContent := range yamlFileContents {
jsonFileContent, err := yaml.YAMLToJSON(yamlFileContent)
if err != nil {
return nil, err
}
policy := new(v1alpha1.Policy)
err = json.Unmarshal(jsonFileContent, policy)
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
}
return policies, nil
}

0 comments on commit ac73bef

Please sign in to comment.