diff --git a/docs/en/platform-engineers/debug-test-cue.md b/docs/en/platform-engineers/debug-test-cue.md index cbda061cfcc6..de3ca3fa563e 100644 --- a/docs/en/platform-engineers/debug-test-cue.md +++ b/docs/en/platform-engineers/debug-test-cue.md @@ -664,3 +664,465 @@ spec: --- ``` + +## Live-Diff the `Application` + +`vela system live-diff` allows users to have a preview of what would change if +upgrade an application. +It basically generates a diff between the specific revision of an application +and the result of `vela system dry-run`. +The result shows the changes (added/modified/removed/no_change) of the application as well as its sub-resources, such as components and traits. +`live-diff` will not make any changes to the living cluster, so it's very +helpful if you want to update an application but worry about the unknown results +that may be produced. + +Let's prepare an application and the updated configuration with which we plan to +update the application. + +```yaml +# app.yaml +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: livediff-demo +spec: + components: + - name: myweb-1 + type: myworker + properties: + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" + traits: + - type: myingress + properties: + domain: "www.example.com" + http: + "/": 80 + - type: myscaler + properties: + replicas: 2 + - name: myweb-2 + type: myworker + properties: + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" +``` + +```yaml +# app-updated.yaml +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: livediff-demo +spec: + components: + - name: myweb-1 + type: myworker + properties: + image: "busybox" + cmd: + - sleep + - "2000" # change a component property + lives: "3" + enemies: "alien" + traits: + - type: myingress + properties: + domain: "www.example.com" + http: + "/": 90 # change a trait + # - type: myscaler # remove a trait + # properties: + # replicas: 2 + - name: myweb-2 + type: myworker + properties: # no change on component property + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" + traits: + - type: myingress # add a trait + properties: + domain: "www.example.com" + http: + "/": 90 + - name: myweb-3 # add a component + type: myworker + properties: + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" + traits: + - type: myingress + properties: + domain: "www.example.com" + http: + "/": 90 +``` +ComponentDefinitions and TraitDefinitions used in this sample are stored in +`./doc/examples/live-diff/definitions`. + +Let's deploy the application. +```shell +kubectl apply ./doc/examples/live-diff/definitions +kubectl apply ./doc/examples/live-diff/app.yaml +``` + +Then we run `live-diff` check out if update with the config we prepared. + +```shell +vela system live-diff -f ./doc/examples/live-diff/app-modified.yaml -r livediff-demo-v1 +``` + +```yaml +--- +# Application (application-sample) has been modified(*) +--- + apiVersion: core.oam.dev/v1beta1 + kind: Application + metadata: + creationTimestamp: null +- name: application-sample ++ name: livediff-demo + namespace: default + spec: + components: + - name: myweb-1 ++ properties: ++ cmd: ++ - sleep ++ - "2000" ++ enemies: alien ++ image: busybox ++ lives: "3" ++ traits: ++ - properties: ++ domain: www.example.com ++ http: ++ /: 90 ++ type: myingress ++ type: myworker ++ - name: myweb-2 + properties: + cmd: + - sleep + - "1000" + enemies: alien + image: busybox + lives: "3" + traits: + - properties: + domain: www.example.com + http: +- /: 80 ++ /: 90 + type: myingress +- - properties: +- replicas: 2 +- type: myscaler + type: myworker +- - name: myweb-2 ++ - name: myweb-3 + properties: + cmd: + - sleep + - "1000" + enemies: alien + image: busybox + lives: "3" ++ traits: ++ - properties: ++ domain: www.example.com ++ http: ++ /: 90 ++ type: myingress + type: myworker + status: + batchRollingState: "" + currentBatch: 0 + rollingState: "" + upgradedReadyReplicas: 0 + upgradedReplicas: 0 + +--- +## Component (myweb-1) has been modified(*) +--- + apiVersion: core.oam.dev/v1alpha2 + kind: Component + metadata: + creationTimestamp: null + labels: +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + name: myweb-1 + spec: + workload: + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app.oam.dev/appRevision: "" + app.oam.dev/component: myweb-1 +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + workload.oam.dev/type: myworker + spec: + selector: + matchLabels: + app.oam.dev/component: myweb-1 + template: + metadata: + labels: + app.oam.dev/component: myweb-1 + spec: + containers: + - command: + - sleep +- - "1000" ++ - "2000" + image: busybox + name: myweb-1 + status: + observedGeneration: 0 + +--- +### Component (myweb-1) / Trait (myingress/ingress) has been modified(*) +--- + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress + metadata: + labels: + app.oam.dev/appRevision: "" + app.oam.dev/component: myweb-1 +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + trait.oam.dev/resource: ingress + trait.oam.dev/type: myingress + name: myweb-1 + spec: + rules: + - host: www.example.com + http: + paths: + - backend: + serviceName: myweb-1 +- servicePort: 80 ++ servicePort: 90 + path: / + +--- +### Component (myweb-1) / Trait (myingress/service) has been modified(*) +--- + apiVersion: v1 + kind: Service + metadata: + labels: + app.oam.dev/appRevision: "" + app.oam.dev/component: myweb-1 +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + trait.oam.dev/resource: service + trait.oam.dev/type: myingress + name: myweb-1 + spec: + ports: +- - port: 80 +- targetPort: 80 ++ - port: 90 ++ targetPort: 90 + selector: + app.oam.dev/component: myweb-1 + +--- +### Component (myweb-1) / Trait (myscaler/scaler) has been removed(-) +--- +- apiVersion: core.oam.dev/v1alpha2 +- kind: ManualScalerTrait +- metadata: +- labels: +- app.oam.dev/appRevision: "" +- app.oam.dev/component: myweb-1 +- app.oam.dev/name: application-sample +- trait.oam.dev/resource: scaler +- trait.oam.dev/type: myscaler +- spec: +- replicaCount: 2 + +--- +## Component (myweb-2) has been modified(*) +--- + apiVersion: core.oam.dev/v1alpha2 + kind: Component + metadata: + creationTimestamp: null + labels: +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + name: myweb-2 + spec: + workload: + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app.oam.dev/appRevision: "" + app.oam.dev/component: myweb-2 +- app.oam.dev/name: application-sample ++ app.oam.dev/name: livediff-demo + workload.oam.dev/type: myworker + spec: + selector: + matchLabels: + app.oam.dev/component: myweb-2 + template: + metadata: + labels: + app.oam.dev/component: myweb-2 + spec: + containers: + - command: + - sleep + - "1000" + image: busybox + name: myweb-2 + status: + observedGeneration: 0 + +--- +### Component (myweb-2) / Trait (myingress/ingress) has been added(+) +--- ++ apiVersion: networking.k8s.io/v1beta1 ++ kind: Ingress ++ metadata: ++ labels: ++ app.oam.dev/appRevision: "" ++ app.oam.dev/component: myweb-2 ++ app.oam.dev/name: livediff-demo ++ trait.oam.dev/resource: ingress ++ trait.oam.dev/type: myingress ++ name: myweb-2 ++ spec: ++ rules: ++ - host: www.example.com ++ http: ++ paths: ++ - backend: ++ serviceName: myweb-2 ++ servicePort: 90 ++ path: / + +--- +### Component (myweb-2) / Trait (myingress/service) has been added(+) +--- ++ apiVersion: v1 ++ kind: Service ++ metadata: ++ labels: ++ app.oam.dev/appRevision: "" ++ app.oam.dev/component: myweb-2 ++ app.oam.dev/name: livediff-demo ++ trait.oam.dev/resource: service ++ trait.oam.dev/type: myingress ++ name: myweb-2 ++ spec: ++ ports: ++ - port: 90 ++ targetPort: 90 ++ selector: ++ app.oam.dev/component: myweb-2 + +--- +## Component (myweb-3) has been added(+) +--- ++ apiVersion: core.oam.dev/v1alpha2 ++ kind: Component ++ metadata: ++ creationTimestamp: null ++ labels: ++ app.oam.dev/name: livediff-demo ++ name: myweb-3 ++ spec: ++ workload: ++ apiVersion: apps/v1 ++ kind: Deployment ++ metadata: ++ labels: ++ app.oam.dev/appRevision: "" ++ app.oam.dev/component: myweb-3 ++ app.oam.dev/name: livediff-demo ++ workload.oam.dev/type: myworker ++ spec: ++ selector: ++ matchLabels: ++ app.oam.dev/component: myweb-3 ++ template: ++ metadata: ++ labels: ++ app.oam.dev/component: myweb-3 ++ spec: ++ containers: ++ - command: ++ - sleep ++ - "1000" ++ image: busybox ++ name: myweb-3 ++ status: ++ observedGeneration: 0 + +--- +### Component (myweb-3) / Trait (myingress/ingress) has been added(+) +--- ++ apiVersion: networking.k8s.io/v1beta1 ++ kind: Ingress ++ metadata: ++ labels: ++ app.oam.dev/appRevision: "" ++ app.oam.dev/component: myweb-3 ++ app.oam.dev/name: livediff-demo ++ trait.oam.dev/resource: ingress ++ trait.oam.dev/type: myingress ++ name: myweb-3 ++ spec: ++ rules: ++ - host: www.example.com ++ http: ++ paths: ++ - backend: ++ serviceName: myweb-3 ++ servicePort: 90 ++ path: / + +--- +### Component (myweb-3) / Trait (myingress/service) has been added(+) +--- ++ apiVersion: v1 ++ kind: Service ++ metadata: ++ labels: ++ app.oam.dev/appRevision: "" ++ app.oam.dev/component: myweb-3 ++ app.oam.dev/name: livediff-demo ++ trait.oam.dev/resource: service ++ trait.oam.dev/type: myingress ++ name: myweb-3 ++ spec: ++ ports: ++ - port: 90 ++ targetPort: 90 ++ selector: ++ app.oam.dev/component: myweb-3 +``` diff --git a/docs/examples/live-diff/app-modified.yaml b/docs/examples/live-diff/app-modified.yaml index 360931c9b317..10597d6362f5 100644 --- a/docs/examples/live-diff/app-modified.yaml +++ b/docs/examples/live-diff/app-modified.yaml @@ -1,31 +1,49 @@ apiVersion: core.oam.dev/v1beta1 kind: Application metadata: - name: application-sample + name: livediff-demo spec: components: - - name: myweb + - name: myweb-1 type: myworker properties: image: "busybox" cmd: - sleep - - "2000" + - "2000" # change a component property lives: "3" enemies: "alien" traits: - type: myingress + properties: + domain: "www.example.com" + http: + "/": 90 # change a trait + # - type: myscaler # remove a trait + # properties: + # replicas: 2 + - name: myweb-2 + type: myworker + properties: # no change on component property + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" + traits: + - type: myingress # add a trait properties: domain: "www.example.com" http: "/": 90 - - name: new-myweb + - name: myweb-3 # add a component type: myworker properties: image: "busybox" cmd: - sleep - - "2000" + - "1000" lives: "3" enemies: "alien" traits: diff --git a/docs/examples/live-diff/app.yaml b/docs/examples/live-diff/app.yaml index bef3a9cfe3fb..ece18c771f29 100644 --- a/docs/examples/live-diff/app.yaml +++ b/docs/examples/live-diff/app.yaml @@ -1,10 +1,10 @@ apiVersion: core.oam.dev/v1beta1 kind: Application metadata: - name: application-sample + name: livediff-demo spec: components: - - name: myweb + - name: myweb-1 type: myworker properties: image: "busybox" @@ -18,4 +18,16 @@ spec: properties: domain: "www.example.com" http: - "/": 80 \ No newline at end of file + "/": 80 + - type: myscaler + properties: + replicas: 2 + - name: myweb-2 + type: myworker + properties: + image: "busybox" + cmd: + - sleep + - "1000" + lives: "3" + enemies: "alien" diff --git a/docs/examples/live-diff/definitions/myscaler.yaml b/docs/examples/live-diff/definitions/myscaler.yaml new file mode 100644 index 000000000000..b259e1a0a2a5 --- /dev/null +++ b/docs/examples/live-diff/definitions/myscaler.yaml @@ -0,0 +1,28 @@ +apiVersion: core.oam.dev/v1beta1 +kind: TraitDefinition +metadata: + annotations: + definition.oam.dev/description: "Configures replicas for your service." + name: myscaler +spec: + appliesToWorkloads: + - webservice + - worker + definitionRef: + name: manualscalertraits.core.oam.dev + workloadRefPath: spec.workloadRef + schematic: + cue: + template: | + outputs: scaler: { + apiVersion: "core.oam.dev/v1alpha2" + kind: "ManualScalerTrait" + spec: { + replicaCount: parameter.replicas + } + } + parameter: { + //+short=r + //+usage=Replicas of the workload + replicas: *1 | int + } diff --git a/references/appfile/dryrun/dryrun.go b/references/appfile/dryrun/dryrun.go index b370017e93f5..17e58dc4503a 100644 --- a/references/appfile/dryrun/dryrun.go +++ b/references/appfile/dryrun/dryrun.go @@ -32,6 +32,7 @@ import ( refappfile "github.com/oam-dev/kubevela/references/appfile" ) +// DryRun executes dry-run on an application type DryRun interface { ExecuteDryRun(ctx context.Context, app *v1beta1.Application, auxiliaries []oam.Object) (*v1alpha2.ApplicationConfiguration, []*v1alpha2.Component, error) } @@ -48,7 +49,7 @@ type Option struct { PackageDiscover *definition.PackageDiscover } -// DryRun simulates applying an application into cluster and returns rendered +// ExecuteDryRun simulates applying an application into cluster and returns rendered // resoures but not persist them into cluster. func (d *Option) ExecuteDryRun(ctx context.Context, app *v1beta1.Application, auxiliaries []oam.Object) (*v1alpha2.ApplicationConfiguration, []*v1alpha2.Component, error) { // TODO(roywang) auxiliaries should not be applied into clusters diff --git a/references/appfile/dryrun/report.go b/references/appfile/dryrun/report.go index f5173304e3fa..2e20f2a44e65 100644 --- a/references/appfile/dryrun/report.go +++ b/references/appfile/dryrun/report.go @@ -36,9 +36,9 @@ var ( func NewReportDiffOption(ctx int, to io.Writer) *ReportDiffOption { return &ReportDiffOption{ DiffMsgs: map[DiffType]string{ - AddDiff: "has been added", - ModifyDiff: "has been modified", - RemoveDiff: "has been removed", + AddDiff: "has been added(+)", + ModifyDiff: "has been modified(*)", + RemoveDiff: "has been removed(-)", NoDiff: "has no change", }, Context: ctx, @@ -56,7 +56,7 @@ type ReportDiffOption struct { // PrintDiffReport formats and prints diff data into target io.Writer // 'app' should be a diifEntry whose top-level is an application func (r *ReportDiffOption) PrintDiffReport(app *DiffEntry) { - _, _ = yellow.Fprintf(r.To, "# Application (%s) %s\n", app.Name, r.DiffMsgs[app.DiffType]) + _, _ = yellow.Fprintf(r.To, "---\n# Application (%s) %s\n---\n", app.Name, r.DiffMsgs[app.DiffType]) printDiffs(app.Diffs, r.Context, r.To) for _, acc := range app.Subs { @@ -64,9 +64,9 @@ func (r *ReportDiffOption) PrintDiffReport(app *DiffEntry) { for _, accSub := range acc.Subs { switch accSub.Kind { case RawCompKind: - _, _ = yellow.Fprintf(r.To, "## Component (%s) %s\n", compName, r.DiffMsgs[accSub.DiffType]) + _, _ = yellow.Fprintf(r.To, "---\n## Component (%s) %s\n---\n", compName, r.DiffMsgs[accSub.DiffType]) case TraitKind: - _, _ = yellow.Fprintf(r.To, "### Component (%s) / Trait (%s) %s\n", compName, accSub.Name, r.DiffMsgs[accSub.DiffType]) + _, _ = yellow.Fprintf(r.To, "---\n### Component (%s) / Trait (%s) %s\n---\n", compName, accSub.Name, r.DiffMsgs[accSub.DiffType]) default: continue }