mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Feat: vela dry-run render results should be affected by override policy and deploy workflowstep (#4815)
* [Feature] vela dry-run render results should be affected by override policy and deploy workflowstep Signed-off-by: cezhang <c1zhang.dev@gmail.com> * multiple input files support; policy,workflow support; new flag: merge orphan policy or workflow Signed-off-by: cezhang <c1zhang.dev@gmail.com> * add more tests Signed-off-by: cezhang <c1zhang.dev@gmail.com> * fix comment issues Signed-off-by: cezhang <c1zhang.dev@gmail.com> * add tests Signed-off-by: cezhang <c1zhang.dev@gmail.com> * fix e2e Signed-off-by: cezhang <c1zhang.dev@gmail.com> * fix tests Signed-off-by: cezhang <c1zhang.dev@gmail.com> Signed-off-by: cezhang <c1zhang.dev@gmail.com>
This commit is contained in:
@@ -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"
|
||||
@@ -167,7 +165,6 @@ 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())
|
||||
applyFile("diff-input-app-with-externals.yaml", "default")
|
||||
applyFile("diff-apprevision.yaml", "default")
|
||||
app := &v1beta1.Application{}
|
||||
@@ -190,7 +187,6 @@ 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-ref-objects.yaml", "vela-system")
|
||||
Expect(runDiff()).Should(ContainSubstring("\"deploy-livediff-demo\" not found"))
|
||||
applyFile("external-workflow.yaml", "default")
|
||||
|
||||
@@ -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"
|
||||
@@ -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")
|
||||
@@ -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.Name, 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
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@ limitations under the License.
|
||||
package dryrun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
|
||||
@@ -59,3 +62,108 @@ 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(testing-app)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
})
|
||||
|
||||
It("Test dry run without custom policy", func() {
|
||||
|
||||
topo, err := os.ReadFile("./testdata/pd-mypolicy.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
var pd v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(topo), &pd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &pd)).Should(BeNil())
|
||||
|
||||
appYAML := readDataFromFile("./testdata/testing-dry-run-4.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) -- Component(testing-dryrun)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app) -- Policy(mypolicy)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: my-policy"))
|
||||
})
|
||||
|
||||
It("Test dry run with trait", func() {
|
||||
|
||||
nocalhost, err := os.ReadFile("../../../charts/vela-core/templates/defwithtemplate/nocalhost.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
nocalhostYAML := strings.Replace(string(nocalhost), "{{ include \"systemDefinitionNamespace\" . }}", types.DefaultKubeVelaNS, 1)
|
||||
var td v1beta1.TraitDefinition
|
||||
Expect(yaml.Unmarshal([]byte(nocalhostYAML), &td)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &td)).Should(BeNil())
|
||||
|
||||
appYAML := readDataFromFile("./testdata/testing-dry-run-5.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) -- Component(testing-dryrun)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("## From the trait nocalhost"))
|
||||
Expect(buff.String()).Should(ContainSubstring("trait.oam.dev/type: nocalhost"))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)}
|
||||
|
||||
|
||||
22
pkg/appfile/dryrun/testdata/pd-mypolicy.yaml
vendored
Normal file
22
pkg/appfile/dryrun/testdata/pd-mypolicy.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/topology.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
name: mypolicy
|
||||
namespace: vela-system
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "testing/v1"
|
||||
kind: "Policy"
|
||||
policy: {
|
||||
name: parameter.name
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
name: string
|
||||
}
|
||||
48
pkg/appfile/dryrun/testdata/testing-dry-run-1.yaml
vendored
Normal file
48
pkg/appfile/dryrun/testdata/testing-dry-run-1.yaml
vendored
Normal file
@@ -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
pkg/appfile/dryrun/testdata/testing-dry-run-2.yaml
vendored
Normal file
27
pkg/appfile/dryrun/testdata/testing-dry-run-2.yaml
vendored
Normal file
@@ -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
pkg/appfile/dryrun/testdata/testing-dry-run-3.yaml
vendored
Normal file
15
pkg/appfile/dryrun/testdata/testing-dry-run-3.yaml
vendored
Normal file
@@ -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
|
||||
|
||||
16
pkg/appfile/dryrun/testdata/testing-dry-run-4.yaml
vendored
Normal file
16
pkg/appfile/dryrun/testdata/testing-dry-run-4.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
policies:
|
||||
- name: mypolicy
|
||||
type: mypolicy
|
||||
properties:
|
||||
name: "my-policy"
|
||||
|
||||
48
pkg/appfile/dryrun/testdata/testing-dry-run-5.yaml
vendored
Normal file
48
pkg/appfile/dryrun/testdata/testing-dry-run-5.yaml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
traits:
|
||||
- type: nocalhost
|
||||
properties:
|
||||
port: 9080
|
||||
gitUrl: https://github.com/nocalhost/bookinfo-productpage.git
|
||||
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/python:3.7.7-slim-productpage-with-pydevd
|
||||
shell: "bash"
|
||||
workDir: "/opt/work"
|
||||
resources:
|
||||
limits:
|
||||
memory: 1Gi
|
||||
cpu: "1"
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: "0.5"
|
||||
debug:
|
||||
remoteDebugPort: 9009
|
||||
hotReload: true
|
||||
sync:
|
||||
type: send
|
||||
filePattern:
|
||||
- ./
|
||||
ignoreFilePattern:
|
||||
- .git
|
||||
- .idea
|
||||
command:
|
||||
run:
|
||||
- sh
|
||||
- run.sh
|
||||
debug:
|
||||
- sh
|
||||
- debug.sh
|
||||
env:
|
||||
- name: "foo"
|
||||
value: "bar"
|
||||
portForward:
|
||||
- 39080:9080
|
||||
|
||||
@@ -20,10 +20,17 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
wfv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/step"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -45,9 +52,10 @@ import (
|
||||
// DryRunCmdOptions contains dry-run cmd options
|
||||
type DryRunCmdOptions struct {
|
||||
cmdutil.IOStreams
|
||||
ApplicationFile string
|
||||
DefinitionFile string
|
||||
OfflineMode bool
|
||||
ApplicationFiles []string
|
||||
DefinitionFile string
|
||||
OfflineMode bool
|
||||
MergeStandaloneFiles bool
|
||||
}
|
||||
|
||||
// NewDryRunCommand creates `dry-run` command
|
||||
@@ -62,8 +70,26 @@ func NewDryRunCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
|
||||
You can also specify a remote url for app:
|
||||
vela dry-run -d /definition/directory/or/file/ -f https://remote-host/app.yaml
|
||||
|
||||
And more, you can specify policy and workflow with application file:
|
||||
vela dry-run -d /definition/directory/or/file/ -f /path/to/app.yaml -f /path/to/policy.yaml -f /path/to/workflow.yaml, OR
|
||||
vela dry-run -d /definition/directory/or/file/ -f /path/to/app.yaml,/path/to/policy.yaml,/path/to/workflow.yaml
|
||||
|
||||
Additionally, if the provided policy and workflow files are not referenced by application file, warning message will show up
|
||||
and those files will be ignored. You can use "merge" flag to make those standalone files effective:
|
||||
vela dry-run -d /definition/directory/or/file/ -f /path/to/app.yaml,/path/to/policy.yaml,/path/to/workflow.yaml --merge
|
||||
|
||||
Limitation:
|
||||
1. Only support one object per file(yaml) for "-f" flag. More support will be added in the future improvement.
|
||||
2. Dry Run with policy and workflow will only take override/topology policies and deploy workflow step into considerations. Other workflow step will be ignored.
|
||||
`,
|
||||
Example: `
|
||||
# dry-run application
|
||||
vela dry-run -f app.yaml
|
||||
|
||||
# dry-run application with policy and workflow
|
||||
vela dry-run -f app.yaml -f policy.yaml -f workflow.yaml
|
||||
`,
|
||||
Example: "vela dry-run",
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeApp,
|
||||
},
|
||||
@@ -88,9 +114,10 @@ You can also specify a remote url for app:
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&o.ApplicationFile, "file", "f", "./app.yaml", "application file name")
|
||||
cmd.Flags().StringSliceVarP(&o.ApplicationFiles, "file", "f", []string{"app.yaml"}, "application related file names")
|
||||
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().BoolVar(&o.MergeStandaloneFiles, "merge", false, "Merge standalone files to produce dry-run results")
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
cmd.SetOut(ioStreams.Out)
|
||||
return cmd
|
||||
@@ -140,23 +167,21 @@ func DryRunApplication(cmdOption *DryRunCmdOptions, c common.Args, namespace str
|
||||
|
||||
// Perform validation only if not in offline mode
|
||||
if !cmdOption.OfflineMode {
|
||||
err = dryRunOpt.ValidateApp(ctx, cmdOption.ApplicationFile)
|
||||
if err != nil {
|
||||
return buff, errors.WithMessagef(err, "validate application: %s by dry-run", cmdOption.ApplicationFile)
|
||||
for _, applicationFile := range cmdOption.ApplicationFiles {
|
||||
err = dryRunOpt.ValidateApp(ctx, applicationFile)
|
||||
if err != nil {
|
||||
return buff, errors.WithMessagef(err, "validate application: %s by dry-run", applicationFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app, err := readApplicationFromFile(cmdOption.ApplicationFile)
|
||||
app, err := readApplicationFromFiles(cmdOption, &buff)
|
||||
if err != nil {
|
||||
return buff, errors.WithMessagef(err, "read application file: %s", cmdOption.ApplicationFile)
|
||||
return buff, errors.WithMessagef(err, "read application files: %s", cmdOption.ApplicationFiles)
|
||||
}
|
||||
|
||||
comps, policies, err := dryRunOpt.ExecuteDryRun(ctx, app)
|
||||
err = dryRunOpt.ExecuteDryRunWithPolicies(ctx, app, &buff)
|
||||
if err != nil {
|
||||
return buff, errors.WithMessage(err, "generate OAM objects")
|
||||
}
|
||||
|
||||
if err = dryRunOpt.PrintDryRun(&buff, app.Name, comps, policies); err != nil {
|
||||
return buff, err
|
||||
}
|
||||
return buff, nil
|
||||
@@ -241,3 +266,140 @@ func readApplicationFromFile(filename string) (*corev1beta1.Application, error)
|
||||
err = json.Unmarshal(fileContent, app)
|
||||
return app, err
|
||||
}
|
||||
|
||||
func readApplicationFromFiles(cmdOption *DryRunCmdOptions, buff *bytes.Buffer) (*corev1beta1.Application, error) {
|
||||
var app *corev1beta1.Application
|
||||
var policies []*v1alpha1.Policy
|
||||
var wf *wfv1alpha1.Workflow
|
||||
policyNameMap := make(map[string]struct{})
|
||||
|
||||
for _, filename := range cmdOption.ApplicationFiles {
|
||||
fileContent, err := utils.ReadRemoteOrLocalPath(filename, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileType := filepath.Ext(filename)
|
||||
switch fileType {
|
||||
case ".yaml", ".yml":
|
||||
// only support one object in one yaml file
|
||||
fileContent, err = yaml.YAMLToJSON(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
// cannot guarantee get the object, but gkv is enough
|
||||
_, gkv, _ := decode(fileContent, nil, nil)
|
||||
|
||||
jsonFileContent, err := yaml.YAMLToJSON(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch *gkv {
|
||||
case corev1beta1.ApplicationKindVersionKind:
|
||||
if app != nil {
|
||||
return nil, errors.New("more than one applications provided")
|
||||
}
|
||||
app = new(corev1beta1.Application)
|
||||
err = json.Unmarshal(jsonFileContent, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case v1alpha1.PolicyGroupVersionKind:
|
||||
policy := new(v1alpha1.Policy)
|
||||
err = json.Unmarshal(jsonFileContent, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies = append(policies, policy)
|
||||
case v1alpha1.WorkflowGroupVersionKind:
|
||||
if wf != nil {
|
||||
return nil, errors.New("more than one external workflow provided")
|
||||
}
|
||||
wf = new(wfv1alpha1.Workflow)
|
||||
err = json.Unmarshal(jsonFileContent, wf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("file %s is not application, policy or workflow", filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only allow one application
|
||||
if app == nil {
|
||||
return nil, errors.New("no application provided")
|
||||
}
|
||||
|
||||
// workflow not referenced by application
|
||||
if !cmdOption.MergeStandaloneFiles {
|
||||
if wf != nil &&
|
||||
((app.Spec.Workflow != nil && app.Spec.Workflow.Ref != wf.Name) || app.Spec.Workflow == nil) {
|
||||
buff.WriteString(fmt.Sprintf("WARNING: workflow %s not referenced by application\n\n", wf.Name))
|
||||
}
|
||||
} else {
|
||||
if wf != nil {
|
||||
app.Spec.Workflow = &corev1beta1.Workflow{
|
||||
Ref: "",
|
||||
Steps: wf.Steps,
|
||||
}
|
||||
}
|
||||
err := getPolicyNameFromWorkflow(wf, policyNameMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
// check standalone policies
|
||||
if _, exist := policyNameMap[policy.Name]; !exist && !cmdOption.MergeStandaloneFiles {
|
||||
buff.WriteString(fmt.Sprintf("WARNING: policy %s not referenced by application\n\n", policy.Name))
|
||||
continue
|
||||
}
|
||||
app.Spec.Policies = append(app.Spec.Policies, corev1beta1.AppPolicy{
|
||||
Name: policy.Name,
|
||||
Type: policy.Type,
|
||||
Properties: policy.Properties,
|
||||
})
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func getPolicyNameFromWorkflow(wf *wfv1alpha1.Workflow, policyNameMap map[string]struct{}) error {
|
||||
|
||||
checkPolicy := func(wfsb wfv1alpha1.WorkflowStepBase, policyNameMap map[string]struct{}) error {
|
||||
workflowStepSpec := &step.DeployWorkflowStepSpec{}
|
||||
if err := utils.StrictUnmarshal(wfsb.Properties.Raw, workflowStepSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range workflowStepSpec.Policies {
|
||||
policyNameMap[p] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if wf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, wfs := range wf.Steps {
|
||||
if wfs.Type == step.DeployWorkflowStep {
|
||||
err := checkPolicy(wfs.WorkflowStepBase, policyNameMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sub := range wfs.SubSteps {
|
||||
if sub.Type == step.DeployWorkflowStep {
|
||||
err = checkPolicy(sub, policyNameMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,24 +17,27 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
wfv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile/dryrun"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
var _ = Describe("Test dry run with policy", func() {
|
||||
It("Test dry run with normal policy", func() {
|
||||
var _ = Describe("Testing dry-run", func() {
|
||||
|
||||
It("Testing dry-run", 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)
|
||||
@@ -42,100 +45,195 @@ var _ = Describe("Test dry run with policy", func() {
|
||||
Expect(yaml.Unmarshal([]byte(webserviceYAML), &wwd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &wwd)).Should(BeNil())
|
||||
|
||||
plcd := v1beta1.PolicyDefinition{}
|
||||
Expect(yaml.Unmarshal([]byte(plcdef), &plcd)).Should(BeNil())
|
||||
plcd.Namespace = types.DefaultKubeVelaNS
|
||||
Expect(k8sClient.Create(context.TODO(), &plcd)).Should(BeNil())
|
||||
app := v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(plcapp), &app)).Should(BeNil())
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
pd, err := c.GetPackageDiscover()
|
||||
scaler, err := os.ReadFile("../../charts/vela-core/templates/defwithtemplate/scaler.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
dm, err := discoverymapper.New(cfg)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
dryRunOpt := dryrun.NewDryRunOption(k8sClient, cfg, dm, pd, nil, false)
|
||||
|
||||
comps, plcs, err := dryRunOpt.ExecuteDryRun(context.TODO(), &app)
|
||||
Expect(err).Should(BeNil())
|
||||
speci := plcs[0].Object["spec"].(map[string]interface{})
|
||||
Expect(speci["service"].(string)).Should(BeEquivalentTo("unified"))
|
||||
buff := bytes.NewBufferString("")
|
||||
Expect(dryRunOpt.PrintDryRun(buff, app.Name, comps, plcs)).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring(`backends:
|
||||
- service: server-v1
|
||||
weight: 80
|
||||
- service: server-v2
|
||||
weight: 20
|
||||
service: unified`))
|
||||
Expect(buff.String()).Should(ContainSubstring("- image: oamdev/hello-world:v1\n name: server-v1"))
|
||||
Expect(buff.String()).Should(ContainSubstring("- image: oamdev/hello-world:v2\n name: server-v2"))
|
||||
})
|
||||
|
||||
It("Test dry run with cue component format", func() {
|
||||
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())
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
opt := DryRunCmdOptions{ApplicationFile: "test-data/dry-run/app.yaml", DefinitionFile: "test-data/dry-run/my-comp.cue", OfflineMode: true}
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-1.yaml"}, OfflineMode: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("name: hello-world"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: hello-world-service"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Service"))
|
||||
Expect(buff.String()).Should(ContainSubstring("replicas: 1"))
|
||||
})
|
||||
|
||||
It("Testing dry-run with policy", func() {
|
||||
deploy, err := os.ReadFile("../../charts/vela-core/templates/defwithtemplate/deploy.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
deployYAML := strings.Replace(string(deploy), "{{ include \"systemDefinitionNamespace\" . }}", types.DefaultKubeVelaNS, 1)
|
||||
var wfsd v1beta1.WorkflowStepDefinition
|
||||
Expect(yaml.Unmarshal([]byte(deployYAML), &wfsd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &wfsd)).Should(BeNil())
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-2.yaml"}, OfflineMode: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app with topology target-default)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
Expect(buff.String()).Should(ContainSubstring("replicas: 1"))
|
||||
})
|
||||
|
||||
It("Testing dry-run with workflow", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-3.yaml"}, OfflineMode: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
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"))
|
||||
})
|
||||
|
||||
It("Testing dry-run with ref workflow", func() {
|
||||
|
||||
policy, err := os.ReadFile("test-data/dry-run/testing-policy.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
var p v1alpha1.Policy
|
||||
Expect(yaml.Unmarshal([]byte(policy), &p)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &p)).Should(BeNil())
|
||||
|
||||
workflow, err := os.ReadFile("test-data/dry-run/testing-wf.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
var wf wfv1alpha1.Workflow
|
||||
Expect(yaml.Unmarshal([]byte(workflow), &wf)).Should(BeNil())
|
||||
Expect(k8sClient.Create(context.TODO(), &wf)).Should(BeNil())
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-4.yaml"}, OfflineMode: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app with topology deploy-somewhere)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
})
|
||||
|
||||
It("Testing dry-run without application provided", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-policy.yaml"}, OfflineMode: false}
|
||||
_, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(err.Error()).Should(ContainSubstring("no application provided"))
|
||||
|
||||
})
|
||||
|
||||
It("Testing dry-run with more than one applications provided", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-1.yaml", "test-data/dry-run/testing-dry-run-2.yaml"}, OfflineMode: false}
|
||||
_, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(err.Error()).Should(ContainSubstring("more than one applications provided"))
|
||||
|
||||
})
|
||||
|
||||
It("Testing dry-run with more than one workflow provided", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-1.yaml", "test-data/dry-run/testing-wf.yaml", "test-data/dry-run/testing-wf.yaml"}, OfflineMode: false}
|
||||
_, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(err.Error()).Should(ContainSubstring("more than one external workflow provided"))
|
||||
|
||||
})
|
||||
|
||||
It("Testing dry-run with unexpected file", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-trait.yaml"}, OfflineMode: false}
|
||||
_, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(err.Error()).Should(ContainSubstring("is not application, policy or workflow"))
|
||||
|
||||
})
|
||||
|
||||
It("Testing dry-run with unexpected file", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-trait.yaml"}, OfflineMode: false}
|
||||
_, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(err.Error()).Should(ContainSubstring("is not application, policy or workflow"))
|
||||
|
||||
})
|
||||
|
||||
It("Testing dry-run merging with external workflow and policy", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-5.yaml", "test-data/dry-run/testing-wf.yaml", "test-data/dry-run/testing-policy.yaml"}, OfflineMode: false, MergeStandaloneFiles: true}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app with topology deploy-somewhere)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
})
|
||||
|
||||
It("Testing dry-run with standalone policy", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-5.yaml", "test-data/dry-run/testing-policy.yaml"}, OfflineMode: false, MergeStandaloneFiles: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("WARNING: policy deploy-somewhere not referenced by application"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
})
|
||||
|
||||
It("Testing dry-run with standalone workflow", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-5.yaml", "test-data/dry-run/testing-wf.yaml"}, OfflineMode: false, MergeStandaloneFiles: false}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("WARNING: workflow testing-wf not referenced by application"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
})
|
||||
|
||||
It("Testing dry-run offline", func() {
|
||||
|
||||
c := common2.Args{}
|
||||
c.SetConfig(cfg)
|
||||
c.SetClient(k8sClient)
|
||||
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-6.yaml"}, DefinitionFile: "test-data/dry-run/testing-worker-def.yaml", OfflineMode: true}
|
||||
buff, err := DryRunApplication(&opt, c, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(buff.String()).Should(ContainSubstring("# Application(testing-app)"))
|
||||
Expect(buff.String()).Should(ContainSubstring("name: testing-dryrun"))
|
||||
Expect(buff.String()).Should(ContainSubstring("kind: Deployment"))
|
||||
Expect(buff.String()).Should(ContainSubstring("workload.oam.dev/type: myworker"))
|
||||
})
|
||||
})
|
||||
|
||||
var plcapp = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: my-test-2
|
||||
spec:
|
||||
components:
|
||||
- name: server-v1
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
- name: server-v2
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v2
|
||||
policies:
|
||||
- type: my-plc
|
||||
name: unified
|
||||
properties:
|
||||
weights:
|
||||
- service: server-v1
|
||||
weight: 80
|
||||
- service: server-v2
|
||||
weight: 20
|
||||
`
|
||||
|
||||
var plcdef = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: My ingress route policy.
|
||||
name: my-plc
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#ServerWeight: {
|
||||
service: string
|
||||
weight: int
|
||||
}
|
||||
parameter: weights: [...#ServerWeight]
|
||||
output: {
|
||||
apiVersion: "split.smi-spec.io/v1alpha3"
|
||||
kind: "TrafficSplit"
|
||||
metadata: name: context.name
|
||||
spec: {
|
||||
service: context.name
|
||||
backends: parameter.weights
|
||||
}
|
||||
}`
|
||||
|
||||
@@ -37,7 +37,9 @@ import (
|
||||
|
||||
// LiveDiffCmdOptions contains the live-diff cmd options
|
||||
type LiveDiffCmdOptions struct {
|
||||
DryRunCmdOptions
|
||||
cmdutil.IOStreams
|
||||
ApplicationFile string
|
||||
DefinitionFile string
|
||||
AppName string
|
||||
Namespace string
|
||||
Revision string
|
||||
@@ -47,10 +49,8 @@ type LiveDiffCmdOptions struct {
|
||||
|
||||
// NewLiveDiffCommand creates `live-diff` command
|
||||
func NewLiveDiffCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
o := &LiveDiffCmdOptions{
|
||||
DryRunCmdOptions: DryRunCmdOptions{
|
||||
IOStreams: ioStreams,
|
||||
}}
|
||||
o := &LiveDiffCmdOptions{IOStreams: ioStreams}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "live-diff",
|
||||
DisableFlagsInUseLine: true,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: vela-app
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: my-comp
|
||||
@@ -1,50 +0,0 @@
|
||||
"my-comp": {
|
||||
annotations: {}
|
||||
attributes: workload: definition: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
}
|
||||
description: "My component."
|
||||
labels: {}
|
||||
type: "component"
|
||||
}
|
||||
template: {
|
||||
output: {
|
||||
metadata: name: "hello-world"
|
||||
spec: {
|
||||
replicas: 1
|
||||
selector: matchLabels: "app.kubernetes.io/name": "hello-world"
|
||||
template: {
|
||||
metadata: labels: "app.kubernetes.io/name": "hello-world"
|
||||
spec: containers: [{
|
||||
name: "hello-world"
|
||||
image: "somefive/hello-world"
|
||||
ports: [{
|
||||
name: "http"
|
||||
containerPort: 80
|
||||
protocol: "TCP"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
}
|
||||
outputs: "hello-world-service": {
|
||||
metadata: name: "hello-world-service"
|
||||
spec: {
|
||||
ports: [{
|
||||
name: "http"
|
||||
protocol: "TCP"
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
}]
|
||||
selector: app: "hello-world"
|
||||
type: "LoadBalancer"
|
||||
}
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
}
|
||||
parameter: {}
|
||||
|
||||
}
|
||||
16
references/cli/test-data/dry-run/testing-dry-run-1.yaml
Normal file
16
references/cli/test-data/dry-run/testing-dry-run-1.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
|
||||
|
||||
20
references/cli/test-data/dry-run/testing-dry-run-2.yaml
Normal file
20
references/cli/test-data/dry-run/testing-dry-run-2.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
policies:
|
||||
- name: target-default
|
||||
type: topology
|
||||
properties:
|
||||
clusters: [ "local" ]
|
||||
namespace: "default"
|
||||
44
references/cli/test-data/dry-run/testing-dry-run-3.yaml
Normal file
44
references/cli/test-data/dry-run/testing-dry-run-3.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
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" ]
|
||||
12
references/cli/test-data/dry-run/testing-dry-run-4.yaml
Normal file
12
references/cli/test-data/dry-run/testing-dry-run-4.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
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:
|
||||
ref: testing-wf
|
||||
11
references/cli/test-data/dry-run/testing-dry-run-5.yaml
Normal file
11
references/cli/test-data/dry-run/testing-dry-run-5.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: webservice
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
|
||||
12
references/cli/test-data/dry-run/testing-dry-run-6.yaml
Normal file
12
references/cli/test-data/dry-run/testing-dry-run-6.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testing-app
|
||||
spec:
|
||||
components:
|
||||
- name: testing-dryrun
|
||||
type: myworker
|
||||
properties:
|
||||
image: oamdev/hello-world:v1
|
||||
|
||||
|
||||
8
references/cli/test-data/dry-run/testing-policy.yaml
Normal file
8
references/cli/test-data/dry-run/testing-policy.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: core.oam.dev/v1alpha1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: deploy-somewhere
|
||||
namespace: default
|
||||
type: topology
|
||||
properties:
|
||||
clusters: ["anywhere"]
|
||||
8
references/cli/test-data/dry-run/testing-trait.yaml
Normal file
8
references/cli/test-data/dry-run/testing-trait.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: bars.example.com
|
||||
namespace: default
|
||||
spec:
|
||||
definitionRef:
|
||||
name: bars.example.com
|
||||
10
references/cli/test-data/dry-run/testing-wf.yaml
Normal file
10
references/cli/test-data/dry-run/testing-wf.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: core.oam.dev/v1alpha1
|
||||
kind: Workflow
|
||||
metadata:
|
||||
name: testing-wf
|
||||
namespace: default
|
||||
steps:
|
||||
- type: deploy
|
||||
name: deploy-somewhere
|
||||
properties:
|
||||
policies: ["deploy-somewhere"]
|
||||
49
references/cli/test-data/dry-run/testing-worker-def.yaml
Normal file
49
references/cli/test-data/dry-run/testing-worker-def.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: myworker
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
}
|
||||
output: {
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user