From b7bf9df55f9a8eeae6535969b6b062507d8da36f Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Fri, 8 Jul 2022 09:38:18 +0800 Subject: [PATCH] Feat: supports multiple difference compare modes (#4334) * Feat: supports multiple difference alignment modes Signed-off-by: barnettZQG * Fix: change field name Signed-off-by: barnettZQG --- .../domain/repository/application.go | 24 ++ pkg/apiserver/domain/service/application.go | 236 +++++++++++------- .../domain/service/application_test.go | 105 ++++++-- pkg/apiserver/event/sync/convert.go | 14 +- pkg/apiserver/interfaces/api/application.go | 8 +- pkg/apiserver/interfaces/api/dto/v1/types.go | 30 ++- 6 files changed, 299 insertions(+), 118 deletions(-) diff --git a/pkg/apiserver/domain/repository/application.go b/pkg/apiserver/domain/repository/application.go index a5fc6354d..e37d4b4b8 100644 --- a/pkg/apiserver/domain/repository/application.go +++ b/pkg/apiserver/domain/repository/application.go @@ -22,6 +22,7 @@ import ( "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) @@ -95,3 +96,26 @@ func DeleteApplicationEnvPolicies(ctx context.Context, store datastore.DataStore } return nil } + +// GetApplicationRevision get the application revision +// If the version is empty, will query the latest revision of the application +func GetApplicationRevision(ctx context.Context, store datastore.DataStore, appName, version string) (*model.ApplicationRevision, error) { + ar := &model.ApplicationRevision{AppPrimaryKey: appName} + if version != "" { + ar.Version = version + } + revisions, err := store.List(ctx, ar, &datastore.ListOptions{ + Page: 1, + PageSize: 1, + SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, + }) + if err != nil || len(revisions) == 0 { + return nil, bcode.ErrApplicationRevisionNotExist + } + latestRevisionRaw := revisions[0] + latestRevision, ok := latestRevisionRaw.(*model.ApplicationRevision) + if !ok { + return nil, errors.New("convert application revision error") + } + return latestRevision, nil +} diff --git a/pkg/apiserver/domain/service/application.go b/pkg/apiserver/domain/service/application.go index ed6ba302e..b0fbd49c4 100644 --- a/pkg/apiserver/domain/service/application.go +++ b/pkg/apiserver/domain/service/application.go @@ -93,7 +93,7 @@ type ApplicationService interface { DetailRevision(ctx context.Context, appName, revisionName string) (*apisv1.DetailRevisionResponse, error) Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) - CompareAppWithLatestRevision(ctx context.Context, app *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) + CompareApp(ctx context.Context, app *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) ResetAppToLatestRevision(ctx context.Context, appName string) (*apisv1.AppResetResponse, error) DryRunAppOrRevision(ctx context.Context, app *model.Application, dryRunReq apisv1.AppDryRunReq) (*apisv1.AppDryRunResponse, error) CreateApplicationTrigger(ctx context.Context, app *model.Application, req apisv1.CreateApplicationTriggerRequest) (*apisv1.ApplicationTriggerBase, error) @@ -295,6 +295,23 @@ func (c *applicationServiceImpl) GetApplicationStatus(ctx context.Context, appmo return &app.Status, nil } +// GetApplicationStatus get application CR from controller cluster +func (c *applicationServiceImpl) GetApplicationCRInEnv(ctx context.Context, appmodel *model.Application, envName string) (*v1beta1.Application, error) { + var app v1beta1.Application + env, err := c.EnvService.GetEnv(ctx, envName) + if err != nil { + return nil, err + } + err = c.KubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appmodel.GetAppNameForSynced()}, &app) + if err != nil { + if apierrors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return &app, nil +} + // GetApplicationCR get application CR in cluster func (c *applicationServiceImpl) GetApplicationCR(ctx context.Context, appModel *model.Application) (*v1beta1.ApplicationList, error) { var apps v1beta1.ApplicationList @@ -629,7 +646,7 @@ func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Applicat // TODO: rollback to handle all the error case // step1: Render oam application version := utils.GenerateVersion("") - oamApp, err := c.renderOAMApplication(ctx, app, req.WorkflowName, version) + oamApp, err := c.renderOAMApplication(ctx, app, req.WorkflowName, "", version) if err != nil { return nil, err } @@ -770,7 +787,7 @@ func (c *applicationServiceImpl) syncConfigs4Application(ctx context.Context, ap return nil } -func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, version string) (*v1beta1.Application, error) { +func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, envName, version string) (*v1beta1.Application, error) { // Priority 1 uses the requested workflow as release . // Priority 2 uses the default workflow as release . var workflow *model.Workflow @@ -786,14 +803,15 @@ func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appMo return nil, err } } - if workflow == nil || workflow.EnvName == "" { - return nil, bcode.ErrWorkflowNotExist + if workflow != nil { + envName = workflow.EnvName } - envbinding, err := c.EnvBindingService.GetEnvBinding(ctx, appModel, workflow.EnvName) + + envbinding, err := c.EnvBindingService.GetEnvBinding(ctx, appModel, envName) if err != nil { return nil, err } - env, err := c.EnvService.GetEnv(ctx, workflow.EnvName) + env, err := c.EnvService.GetEnv(ctx, envName) if err != nil { return nil, err } @@ -896,25 +914,25 @@ func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appMo } app.Spec.Policies = append(app.Spec.Policies, appPolicy) } - - app.Annotations[oam.AnnotationWorkflowName] = workflow.Name - var steps []v1beta1.WorkflowStep - for _, step := range workflow.Steps { - var workflowStep = v1beta1.WorkflowStep{ - Name: step.Name, - Type: step.Type, - Inputs: step.Inputs, - Outputs: step.Outputs, + if workflow != nil { + app.Annotations[oam.AnnotationWorkflowName] = workflow.Name + var steps []v1beta1.WorkflowStep + for _, step := range workflow.Steps { + var workflowStep = v1beta1.WorkflowStep{ + Name: step.Name, + Type: step.Type, + Inputs: step.Inputs, + Outputs: step.Outputs, + } + if step.Properties != nil { + workflowStep.Properties = step.Properties.RawExtension() + } + steps = append(steps, workflowStep) } - if step.Properties != nil { - workflowStep.Properties = step.Properties.RawExtension() + app.Spec.Workflow = &v1beta1.Workflow{ + Steps: steps, } - steps = append(steps, workflowStep) } - app.Spec.Workflow = &v1beta1.Workflow{ - Steps: steps, - } - return app, nil } @@ -1378,50 +1396,101 @@ func (c *applicationServiceImpl) Statistics(ctx context.Context, app *model.Appl }, nil } -// CompareAppWithLatestRevision compare application with last revision -func (c *applicationServiceImpl) CompareAppWithLatestRevision(ctx context.Context, appModel *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) { - var reqWorkflowName string - if compareReq.Env != "" { - reqWorkflowName = repository.ConvertWorkflowName(compareReq.Env) - } - newApp, err := c.renderOAMApplication(ctx, appModel, reqWorkflowName, "") - if err != nil { - return nil, err - } - ignoreSomeParams(newApp) - newAppBytes, err := yaml.Marshal(newApp) - if err != nil { - return nil, err +// CompareApp compare application +func (c *applicationServiceImpl) CompareApp(ctx context.Context, appModel *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) { + var base, compareTarget *v1beta1.Application + var err error + var envNameByRevision string + switch { + case compareReq.CompareLatestWithRunning != nil: + base, err = c.renderOAMApplication(ctx, appModel, "", compareReq.CompareLatestWithRunning.Env, "") + if err != nil { + log.Logger.Errorf("failed to build the latest application %s", err.Error()) + break + } + case compareReq.CompareRevisionWithRunning != nil || compareReq.CompareRevisionWithLatest != nil: + var revision = "" + if compareReq.CompareRevisionWithRunning != nil { + revision = compareReq.CompareRevisionWithRunning.Revision + } + if compareReq.CompareRevisionWithLatest != nil { + revision = compareReq.CompareRevisionWithLatest.Revision + } + base, envNameByRevision, err = c.getAppModelFromRevision(ctx, appModel.Name, revision) + if err != nil { + log.Logger.Errorf("failed to get the app model from the revision %s", err.Error()) + break + } } - oldApp, err := c.getAppFromLatestRevision(ctx, appModel.Name, compareReq.Env, "") - if err != nil { - if errors.Is(err, bcode.ErrApplicationRevisionNotExist) { - return &apisv1.AppCompareResponse{IsDiff: false, NewAppYAML: string(newAppBytes)}, nil + switch { + case compareReq.CompareLatestWithRunning != nil || compareReq.CompareRevisionWithRunning != nil: + var envName string + if compareReq.CompareLatestWithRunning != nil { + envName = compareReq.CompareLatestWithRunning.Env + } + if compareReq.CompareRevisionWithRunning != nil { + envName = envNameByRevision + } + if envName == "" { + break + } + compareTarget, err = c.GetApplicationCRInEnv(ctx, appModel, envName) + if err != nil { + log.Logger.Errorf("failed to query the application CR %s", err.Error()) + break + } + case compareReq.CompareRevisionWithLatest != nil: + compareTarget, err = c.renderOAMApplication(ctx, appModel, "", envNameByRevision, "") + if err != nil { + log.Logger.Errorf("failed to build the latest application %s", err.Error()) + break } - return nil, err } - ignoreSomeParams(oldApp) - oldAppBytes, err := yaml.Marshal(oldApp) - if err != nil { - return nil, err + + var baseAppBytes, targetAppBytes []byte + + if base != nil { + ignoreSomeParams(base) + baseAppBytes, err = yaml.Marshal(base) + if err != nil { + return nil, err + } } + + if compareTarget != nil { + ignoreSomeParams(compareTarget) + targetAppBytes, err = yaml.Marshal(compareTarget) + if err != nil { + return nil, err + } + } + + compareResponse := &apisv1.AppCompareResponse{IsDiff: true, BaseAppYAML: string(baseAppBytes), TargetAppYAML: string(targetAppBytes)} + + if base == nil || compareTarget == nil { + return compareResponse, nil + } + args := common2.Args{ Schema: common2.Scheme, } _ = args.SetConfig(c.KubeConfig) args.SetClient(c.KubeClient) - diffResult, buff, err := compare(ctx, args, newApp, oldApp) + diffResult, buff, err := compare(ctx, args, compareTarget, base) if err != nil { log.Logger.Errorf("fail to compare the app %s", err.Error()) - return &apisv1.AppCompareResponse{IsDiff: false, NewAppYAML: string(newAppBytes), OldAppYAML: string(oldAppBytes)}, err + compareResponse.IsDiff = false + return compareResponse, nil } - return &apisv1.AppCompareResponse{IsDiff: diffResult.DiffType != "", DiffReport: buff.String(), NewAppYAML: string(newAppBytes), OldAppYAML: string(oldAppBytes)}, nil + compareResponse.IsDiff = diffResult.DiffType != "" + compareResponse.DiffReport = buff.String() + return compareResponse, nil } // ResetAppToLatestRevision reset app's component to last revision func (c *applicationServiceImpl) ResetAppToLatestRevision(ctx context.Context, appName string) (*apisv1.AppResetResponse, error) { - targetApp, err := c.getAppFromLatestRevision(ctx, appName, "", "") + targetApp, _, err := c.getAppModelFromRevision(ctx, appName, "") if err != nil { return nil, err } @@ -1433,16 +1502,12 @@ func (c *applicationServiceImpl) DryRunAppOrRevision(ctx context.Context, appMod var app *v1beta1.Application var err error if dryRunReq.DryRunType == "APP" { - var reqWorkflowName string - if dryRunReq.Env != "" { - reqWorkflowName = repository.ConvertWorkflowName(dryRunReq.Env) - } - app, err = c.renderOAMApplication(ctx, appModel, reqWorkflowName, "") + app, err = c.renderOAMApplication(ctx, appModel, dryRunReq.Workflow, dryRunReq.Env, "") if err != nil { return nil, err } } else { - app, err = c.getAppFromLatestRevision(ctx, dryRunReq.AppName, dryRunReq.Env, dryRunReq.Version) + app, _, err = c.getAppModelFromRevision(ctx, dryRunReq.AppName, dryRunReq.Version) if err != nil { return nil, err } @@ -1470,33 +1535,16 @@ func genWebhookToken() string { return string(b) } -func (c *applicationServiceImpl) getAppFromLatestRevision(ctx context.Context, appName string, envName string, version string) (*v1beta1.Application, error) { - - ar := &model.ApplicationRevision{AppPrimaryKey: appName} - if envName != "" { - ar.EnvName = envName - } - if version != "" { - ar.Version = version - } - revisions, err := c.Store.List(ctx, ar, &datastore.ListOptions{ - Page: 1, - PageSize: 1, - SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, - }) - if err != nil || len(revisions) == 0 { - return nil, bcode.ErrApplicationRevisionNotExist - } - latestRevisionRaw := revisions[0] - latestRevision, ok := latestRevisionRaw.(*model.ApplicationRevision) - if !ok { - return nil, errors.New("convert application revision error") +func (c *applicationServiceImpl) getAppModelFromRevision(ctx context.Context, appName string, version string) (*v1beta1.Application, string, error) { + latestRevision, err := repository.GetApplicationRevision(ctx, c.Store, appName, version) + if err != nil { + return nil, "", err } oldApp := &v1beta1.Application{} if err := yaml.Unmarshal([]byte(latestRevision.ApplyAppConfig), oldApp); err != nil { - return nil, err + return nil, "", err } - return oldApp, nil + return oldApp, latestRevision.EnvName, nil } func (c *applicationServiceImpl) resetApp(ctx context.Context, targetApp *v1beta1.Application) (*apisv1.AppResetResponse, error) { @@ -1605,21 +1653,23 @@ func dryRunApplication(ctx context.Context, c common2.Args, app *v1beta1.Applica // ignoreSomeParams ignore some parameters before comparing the app changes. // ignore the workflow spec func ignoreSomeParams(o *v1beta1.Application) { - // set default - o.ResourceVersion = "" - o.Spec.Workflow = nil - newAnnotations := map[string]string{} - annotations := o.GetAnnotations() - for k, v := range annotations { - if k == oam.AnnotationDeployVersion || k == oam.AnnotationPublishVersion || k == "kubectl.kubernetes.io/last-applied-configuration" { - continue - } - newAnnotations[k] = v - } - o.SetAnnotations(newAnnotations) + var defaultApplication = v1beta1.Application{} + // only compare the spec without the workflow + defaultApplication.Spec = o.Spec + defaultApplication.Spec.Workflow = nil + defaultApplication.Name = o.Name + defaultApplication.Namespace = o.Namespace + + sort.Slice(defaultApplication.Spec.Policies, func(i, j int) bool { + return defaultApplication.Spec.Policies[i].Name < defaultApplication.Spec.Policies[j].Name + }) + sort.Slice(defaultApplication.Spec.Components, func(i, j int) bool { + return defaultApplication.Spec.Components[i].Name < defaultApplication.Spec.Components[j].Name + }) + *o = defaultApplication } -func compare(ctx context.Context, c common2.Args, newApp *v1beta1.Application, oldApp *v1beta1.Application) (*dryrun.DiffEntry, bytes.Buffer, error) { +func compare(ctx context.Context, c common2.Args, targetApp *v1beta1.Application, baseApp *v1beta1.Application) (*dryrun.DiffEntry, bytes.Buffer, error) { var buff = bytes.Buffer{} _, err := c.GetClient() if err != nil { @@ -1643,7 +1693,7 @@ func compare(ctx context.Context, c common2.Args, newApp *v1beta1.Application, o return nil, buff, err } liveDiffOption := dryrun.NewLiveDiffOption(client, config, dm, pd, objs) - diffResult, err := liveDiffOption.DiffApps(ctx, newApp, oldApp) + diffResult, err := liveDiffOption.DiffApps(ctx, baseApp, targetApp) if err != nil { return nil, buff, err } diff --git a/pkg/apiserver/domain/service/application_test.go b/pkg/apiserver/domain/service/application_test.go index 87504fcb8..8f125d831 100644 --- a/pkg/apiserver/domain/service/application_test.go +++ b/pkg/apiserver/domain/service/application_test.go @@ -478,8 +478,12 @@ var _ = Describe("Test application service function", func() { Expect(resp.Total).Should(Equal(int64(3))) }) - It("Test CompareAppWithLatestRevision function", func() { - + It("Test CompareApp function", func() { + check := func(compareResponse *v1.AppCompareResponse, isDiff bool) { + Expect(cmp.Diff(compareResponse.BaseAppYAML, "")).ShouldNot(BeEmpty()) + Expect(cmp.Diff(compareResponse.TargetAppYAML, "")).ShouldNot(BeEmpty()) + Expect(cmp.Diff(compareResponse.IsDiff, isDiff)).Should(BeEmpty()) + } appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) _, err = appService.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{WorkflowName: repository.ConvertWorkflowName("app-dev")}) @@ -488,23 +492,40 @@ var _ = Describe("Test application service function", func() { Expect(err).Should(BeNil()) By("compare when app not change, should return false") - compareResponse, err := appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err := appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithRunning: &v1.CompareRevisionWithRunningOption{}, + }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) + check(compareResponse, false) - By("compare when app not change and env not empty, should return false") - compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{Env: "app-dev"}) + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithLatest: &v1.CompareRevisionWithLatestOption{}, + }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) + check(compareResponse, false) + + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-dev", + }, + }) + Expect(err).Should(BeNil()) + check(compareResponse, false) By("compare when app add env, not change, should return false") _, err = envService.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-prod", Namespace: "envnsprod", Targets: []string{defaultTarget}, Project: "app-prod"}) Expect(err).Should(BeNil()) _, err = envBindingService.CreateEnvBinding(context.TODO(), appModel, v1.CreateApplicationEnvbindingRequest{EnvBinding: v1.EnvBinding{Name: "app-prod"}}) Expect(err).Should(BeNil()) - compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-prod", + }, + }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) + Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) + Expect(cmp.Diff(compareResponse.TargetAppYAML, "")).Should(BeEmpty()) + Expect(cmp.Diff(compareResponse.BaseAppYAML, "")).ShouldNot(BeEmpty()) By("compare when app's env add target, should return true") _, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}}) @@ -515,9 +536,13 @@ var _ = Describe("Test application service function", func() { Targets: []string{defaultTarget, "dev-target1"}, }) Expect(err).Should(BeNil()) - compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-dev", + }, + }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) + check(compareResponse, true) By("compare when update app's trait, should return true") // reset app config @@ -529,11 +554,21 @@ var _ = Describe("Test application service function", func() { Description: "description", }) Expect(err).Should(BeNil()) - compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithLatest: &v1.CompareRevisionWithLatestOption{}, + }) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) - By("compare when update component's target after app deployed ,should return ture") + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-dev", + }, + }) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) + + By("compare when update component's target after app deployed ,should return true") // reset app config _, err = appService.ResetAppToLatestRevision(context.TODO(), testApp) Expect(err).Should(BeNil()) @@ -545,9 +580,49 @@ var _ = Describe("Test application service function", func() { Properties: &newProperties, }) Expect(err).Should(BeNil()) - compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithLatest: &v1.CompareRevisionWithLatestOption{}, + }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) + check(compareResponse, true) + + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-dev", + }, + }) + Expect(err).Should(BeNil()) + check(compareResponse, true) + + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithRunning: &v1.CompareRevisionWithRunningOption{}, + }) + + Expect(err).Should(BeNil()) + check(compareResponse, false) + + By("compare when changed the application CR, should return true") + + appCR, err := appService.GetApplicationCRInEnv(context.TODO(), appModel, "app-dev") + Expect(err).Should(BeNil()) + appCR.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte("{\"exposeType\":\"NodePort\",\"image\":\"nginx:222\",\"imagePullPolicy\":\"Always\"}")} + err = k8sClient.Update(context.TODO(), appCR) + Expect(err).Should(BeNil()) + + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareRevisionWithRunning: &v1.CompareRevisionWithRunningOption{}, + }) + Expect(err).Should(BeNil()) + check(compareResponse, true) + + compareResponse, err = appService.CompareApp(context.TODO(), appModel, v1.AppCompareReq{ + CompareLatestWithRunning: &v1.CompareLatestWithRunningOption{ + Env: "app-dev", + }, + }) + Expect(err).Should(BeNil()) + check(compareResponse, true) + err = envBindingService.ApplicationEnvRecycle(context.TODO(), &model.Application{Name: testApp}, &model.EnvBinding{Name: "app-dev"}) Expect(err).Should(BeNil()) }) diff --git a/pkg/apiserver/event/sync/convert.go b/pkg/apiserver/event/sync/convert.go index d19d78517..3d231bdcb 100644 --- a/pkg/apiserver/event/sync/convert.go +++ b/pkg/apiserver/event/sync/convert.go @@ -23,6 +23,9 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" "github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert" @@ -98,8 +101,17 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1. targetNames = append(targetNames, name) } } + var namespace corev1.Namespace + envName := model.AutoGenEnvNamePrefix + targetApp.Namespace + // Get the env name from the label of namespace + // If the namespace created by `vela env init` + if c.cli.Get(ctx, types.NamespacedName{Name: targetApp.Namespace}, &namespace) == nil { + if env, ok := namespace.Labels[oam.LabelNamespaceOfEnvName]; ok { + envName = env + } + } dsApp.Env = &model.Env{ - Name: model.AutoGenEnvNamePrefix + targetApp.Namespace, + Name: envName, Namespace: targetApp.Namespace, Description: model.AutoGenDesc, Project: newProject, diff --git a/pkg/apiserver/interfaces/api/application.go b/pkg/apiserver/interfaces/api/application.go index 67c562470..9efb83be9 100644 --- a/pkg/apiserver/interfaces/api/application.go +++ b/pkg/apiserver/interfaces/api/application.go @@ -549,8 +549,8 @@ func (c *applicationAPIInterface) GetWebServiceRoute() *restful.WebService { Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.ListWorkflowRecordsResponse{})) - ws.Route(ws.POST("/{appName}/compare").To(c.compareAppWithLatestRevision). - Doc("compare application with env latest revision"). + ws.Route(ws.POST("/{appName}/compare").To(c.compareApp). + Doc("compare application"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.RbacService.CheckPerm("application", "compare")). Filter(c.appCheckFilter). @@ -1184,7 +1184,7 @@ func (c *applicationAPIInterface) listApplicationRecords(req *restful.Request, r } } -func (c *applicationAPIInterface) compareAppWithLatestRevision(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) compareApp(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var compareReq apis.AppCompareReq @@ -1197,7 +1197,7 @@ func (c *applicationAPIInterface) compareAppWithLatestRevision(req *restful.Requ return } - base, err := c.ApplicationService.CompareAppWithLatestRevision(req.Request.Context(), app, compareReq) + base, err := c.ApplicationService.CompareApp(req.Request.Context(), app, compareReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/interfaces/api/dto/v1/types.go b/pkg/apiserver/interfaces/api/dto/v1/types.go index d1345e7d5..e5784c33f 100644 --- a/pkg/apiserver/interfaces/api/dto/v1/types.go +++ b/pkg/apiserver/interfaces/api/dto/v1/types.go @@ -362,10 +362,10 @@ type ApplicationBase struct { // AppCompareResponse application compare result type AppCompareResponse struct { - IsDiff bool `json:"isDiff"` - DiffReport string `json:"diffReport"` - NewAppYAML string `json:"newAppYAML"` - OldAppYAML string `json:"oldAppYAML"` + IsDiff bool `json:"isDiff"` + DiffReport string `json:"diffReport"` + BaseAppYAML string `json:"baseAppYAML"` + TargetAppYAML string `json:"targetAppYAML"` } // AppResetResponse application reset result @@ -375,7 +375,26 @@ type AppResetResponse struct { // AppCompareReq application compare req type AppCompareReq struct { - Env string `json:"env"` + CompareRevisionWithRunning *CompareRevisionWithRunningOption `json:"compareRevisionWithRunning,omitempty"` + CompareRevisionWithLatest *CompareRevisionWithLatestOption `json:"compareRevisionWithLatest,omitempty"` + CompareLatestWithRunning *CompareLatestWithRunningOption `json:"compareLatestWithRunning,omitempty"` +} + +// CompareRevisionWithRunningOption means compare the specified version with the application in cluster. +type CompareRevisionWithRunningOption struct { + // Revision, If not specified, means use the latest revision. + Revision string `json:"revision" optional:"true"` +} + +// CompareRevisionWithLatestOption means compare the the specified version with the latest application configuration +type CompareRevisionWithLatestOption struct { + // Revision, If not specified, means use the latest revision. + Revision string `json:"revision" optional:"true"` +} + +// CompareLatestWithRunningOption means compare the latest configuration with the app in cluster. +type CompareLatestWithRunningOption struct { + Env string `json:"env" validate:"required"` } // AppDryRunReq application dry-run req @@ -383,6 +402,7 @@ type AppDryRunReq struct { AppName string `json:"appName"` DryRunType string `json:"dryRunType"` Env string `json:"env"` + Workflow string `json:"workflow"` Version string `json:"version"` }