Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: vela dry-run render results should be affected by override policy and deploy workflowstep #4815

Merged
merged 8 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions pkg/appfile/dryrun/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -167,7 +165,7 @@ var _ = Describe("Test Live-Diff", func() {
Expect(k8sClient.Create(context.Background(), un)).Should(Succeed())
}
ctx := context.Background()
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(Succeed())
//Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(Succeed())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason for commenting them? You can remove them if they are not in use anymore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already move to suite_test BeforeSuite function, my testcase needs this setting as well. Cannot guarantee which testcase invoked first, if both testcase have this setting, error show.

Somefive marked this conversation as resolved.
Show resolved Hide resolved
applyFile("diff-input-app-with-externals.yaml", "default")
applyFile("diff-apprevision.yaml", "default")
app := &v1beta1.Application{}
Expand All @@ -190,7 +188,7 @@ var _ = Describe("Test Live-Diff", func() {
Expect(runDiff()).Should(ContainSubstring("\"myworker\" not found"))
applyFile("td-myingress.yaml", "vela-system")
applyFile("cd-myworker.yaml", "vela-system")
applyFile("wd-deploy.yaml", "vela-system")
//applyFile("wd-deploy.yaml", "vela-system")
Somefive marked this conversation as resolved.
Show resolved Hide resolved
applyFile("wd-ref-objects.yaml", "vela-system")
Expect(runDiff()).Should(ContainSubstring("\"deploy-livediff-demo\" not found"))
applyFile("external-workflow.yaml", "default")
Expand Down
123 changes: 123 additions & 0 deletions pkg/appfile/dryrun/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
"os"
"path/filepath"

"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"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -143,6 +148,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 +212,120 @@ func (d *Option) PrintDryRun(buff *bytes.Buffer, appName string, comps []*types.
}
return nil
}

// ExecuteDryRunWithPolicies is similar to ExecuteDryRun func, but considers deploy workflow step and topology+override policies
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)
if err != nil {
return err
}
comps, pms, err := d.ExecuteDryRun(ctx, patchedApp)
if err != nil {
return err
}
err = d.PrintDryRun(buff, fmt.Sprintf("%s only with override policies", 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
}
67 changes: 67 additions & 0 deletions pkg/appfile/dryrun/dryrun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package dryrun

import (
"bytes"
"context"
"encoding/json"
"os"
"strings"

"github.com/oam-dev/kubevela/apis/types"

Expand Down Expand Up @@ -59,3 +62,67 @@ var _ = Describe("Test DryRun", func() {
Expect(diff).Should(BeEmpty())
})
})

var _ = Describe("Test dry run with policies", func() {
It("Test dry run with override policy", func() {

webservice, err := os.ReadFile("../../../charts/vela-core/templates/defwithtemplate/webservice.yaml")
Expect(err).Should(BeNil())
webserviceYAML := strings.Replace(string(webservice), "{{ include \"systemDefinitionNamespace\" . }}", types.DefaultKubeVelaNS, 1)
wwd := v1beta1.ComponentDefinition{}
Expect(yaml.Unmarshal([]byte(webserviceYAML), &wwd)).Should(BeNil())
Expect(k8sClient.Create(context.TODO(), &wwd)).Should(BeNil())

scaler, err := os.ReadFile("../../../charts/vela-core/templates/defwithtemplate/scaler.yaml")
Expect(err).Should(BeNil())
scalerYAML := strings.Replace(string(scaler), "{{ include \"systemDefinitionNamespace\" . }}", types.DefaultKubeVelaNS, 1)
var td v1beta1.TraitDefinition
Expect(yaml.Unmarshal([]byte(scalerYAML), &td)).Should(BeNil())
Expect(k8sClient.Create(context.TODO(), &td)).Should(BeNil())

appYAML := readDataFromFile("./testdata/testing-dry-run-1.yaml")
app := &v1beta1.Application{}
Expect(yaml.Unmarshal([]byte(appYAML), &app)).Should(BeNil())

var buff = bytes.Buffer{}
err = dryrunOpt.ExecuteDryRunWithPolicies(context.TODO(), app, &buff)
Expect(err).Should(BeNil())
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app with topology target-default)"))
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app with topology target-prod)"))
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
Expect(buff.String()).Should(ContainSubstring("replicas: 1"))
Expect(buff.String()).Should(ContainSubstring("replicas: 3"))
Expect(buff.String()).Should(ContainSubstring("kind: Service"))
})

It("Test dry run only with override policy", func() {

appYAML := readDataFromFile("./testdata/testing-dry-run-2.yaml")
app := &v1beta1.Application{}
Expect(yaml.Unmarshal([]byte(appYAML), &app)).Should(BeNil())

var buff = bytes.Buffer{}
err := dryrunOpt.ExecuteDryRunWithPolicies(context.TODO(), app, &buff)
Expect(err).Should(BeNil())
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app only with override policies)"))
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
Expect(buff.String()).Should(ContainSubstring("replicas: 3"))
Expect(buff.String()).Should(ContainSubstring("kind: Service"))
})

It("Test dry run without deploy workflow", func() {

appYAML := readDataFromFile("./testdata/testing-dry-run-3.yaml")
app := &v1beta1.Application{}
Expect(yaml.Unmarshal([]byte(appYAML), &app)).Should(BeNil())

var buff = bytes.Buffer{}
err := dryrunOpt.ExecuteDryRunWithPolicies(context.TODO(), app, &buff)
Expect(err).Should(BeNil())
Expect(buff.String()).Should(ContainSubstring("# Application(default)"))
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
})
})
18 changes: 18 additions & 0 deletions pkg/appfile/dryrun/suit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@ limitations under the License.
package dryrun

import (
"context"
"os"
"path/filepath"
"testing"
"time"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"

"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

Expand Down Expand Up @@ -103,6 +111,16 @@ var _ = BeforeSuite(func(done Done) {
tdMyIngress, err := oamutil.Object2Unstructured(myingressDef)
Expect(err).Should(BeNil())

// create vela-system ns
Expect(k8sClient.Create(context.TODO(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: types.DefaultKubeVelaNS}})).Should(Succeed())
// create deploy workflow step definition
deploy, err := os.ReadFile("./testdata/wd-deploy.yaml")
Expect(err).Should(BeNil())
var wfsd v1beta1.WorkflowStepDefinition
Expect(yaml.Unmarshal([]byte(deploy), &wfsd)).Should(BeNil())
wfsd.SetNamespace(types.DefaultKubeVelaNS)
Expect(k8sClient.Create(context.TODO(), &wfsd)).Should(BeNil())

dryrunOpt = NewDryRunOption(k8sClient, cfg, dm, pd, []oam.Object{cdMyWorker, tdMyIngress}, false)
diffOpt = &LiveDiffOption{DryRun: dryrunOpt, Parser: appfile.NewApplicationParser(k8sClient, dm, pd)}

Expand Down
48 changes: 48 additions & 0 deletions pkg/appfile/dryrun/testdata/testing-dry-run-1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: testing-app
namespace: default
spec:
components:
- name: testing-dryrun
type: webservice
properties:
image: oamdev/hello-world:v1
ports:
- port: 8000
expose: true
traits:
- type: scaler
properties:
replicas: 1
policies:
- name: target-default
type: topology
properties:
clusters: [ "local" ]
namespace: "default"
- name: target-prod
type: topology
properties:
clusters: [ "local" ]
namespace: "prod"
- name: deploy-ha
type: override
properties:
components:
- type: webservice
traits:
- type: scaler
properties:
replicas: 3
workflow:
steps:
- name: deploy2default
type: deploy
properties:
policies: [ "target-default" ]
- name: deploy2prod
type: deploy
properties:
policies: [ "target-prod", "deploy-ha" ]
27 changes: 27 additions & 0 deletions pkg/appfile/dryrun/testdata/testing-dry-run-2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: testing-app
spec:
components:
- name: testing-dryrun
type: webservice
properties:
image: oamdev/hello-world:v1
ports:
- port: 8000
expose: true
traits:
- type: scaler
properties:
replicas: 1
policies:
- name: deploy-ha
type: override
properties:
components:
- type: webservice
traits:
- type: scaler
properties:
replicas: 3
15 changes: 15 additions & 0 deletions pkg/appfile/dryrun/testdata/testing-dry-run-3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: testing-app
spec:
components:
- name: testing-dryrun
type: webservice
properties:
image: oamdev/hello-world:v1
workflow:
steps:
- name: suspend
type: suspend