github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/revision.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package application 18 19 import ( 20 "context" 21 "sort" 22 23 "github.com/hashicorp/go-version" 24 "github.com/kubevela/pkg/util/k8s" 25 "github.com/pkg/errors" 26 corev1 "k8s.io/api/core/v1" 27 apiequality "k8s.io/apimachinery/pkg/api/equality" 28 apierrors "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 ktypes "k8s.io/apimachinery/pkg/types" 31 utilfeature "k8s.io/apiserver/pkg/util/feature" 32 "k8s.io/klog/v2" 33 "k8s.io/utils/pointer" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 36 "github.com/kubevela/pkg/util/compression" 37 38 "github.com/kubevela/pkg/controller/sharding" 39 monitorContext "github.com/kubevela/pkg/monitor/context" 40 workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1" 41 42 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 43 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" 44 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 45 "github.com/oam-dev/kubevela/pkg/appfile" 46 "github.com/oam-dev/kubevela/pkg/cache" 47 "github.com/oam-dev/kubevela/pkg/component" 48 "github.com/oam-dev/kubevela/pkg/controller/utils" 49 "github.com/oam-dev/kubevela/pkg/features" 50 "github.com/oam-dev/kubevela/pkg/monitor/metrics" 51 "github.com/oam-dev/kubevela/pkg/oam" 52 "github.com/oam-dev/kubevela/pkg/oam/util" 53 ) 54 55 type contextKey int 56 57 var ( 58 // DisableAllComponentRevision disable component revision creation 59 DisableAllComponentRevision = false 60 // DisableAllApplicationRevision disable application revision creation 61 DisableAllApplicationRevision = false 62 ) 63 64 func contextWithComponentNamespace(ctx context.Context, ns string) context.Context { 65 return context.WithValue(ctx, ComponentNamespaceContextKey, ns) 66 } 67 68 func componentNamespaceFromContext(ctx context.Context) string { 69 ns, _ := ctx.Value(ComponentNamespaceContextKey).(string) 70 return ns 71 } 72 73 func contextWithReplicaKey(ctx context.Context, key string) context.Context { 74 return context.WithValue(ctx, ReplicaKeyContextKey, key) 75 } 76 77 func replicaKeyFromContext(ctx context.Context) string { 78 key, _ := ctx.Value(ReplicaKeyContextKey).(string) 79 return key 80 } 81 82 // PrepareCurrentAppRevision will generate a pure revision without metadata and rendered result 83 // the generated revision will be compare with the last revision to see if there's any difference. 84 func (h *AppHandler) PrepareCurrentAppRevision(ctx context.Context, af *appfile.Appfile) error { 85 if ctx, ok := ctx.(monitorContext.Context); ok { 86 subCtx := ctx.Fork("prepare-current-appRevision", monitorContext.DurationMetric(func(v float64) { 87 metrics.AppReconcileStageDurationHistogram.WithLabelValues("prepare-current-apprev").Observe(v) 88 })) 89 defer subCtx.Commit("finish prepare current appRevision") 90 } 91 92 if af.AppRevision != nil { 93 h.isNewRevision = false 94 h.latestAppRev = af.AppRevision 95 h.currentAppRev = af.AppRevision 96 h.currentRevHash = af.AppRevisionHash 97 return nil 98 } 99 100 appRev, appRevisionHash, err := h.gatherRevisionSpec(af) 101 if err != nil { 102 return err 103 } 104 h.currentAppRev = appRev 105 h.currentRevHash = appRevisionHash 106 if err := h.getLatestAppRevision(ctx); err != nil { 107 return err 108 } 109 110 var needGenerateRevision bool 111 h.isNewRevision, needGenerateRevision, err = h.currentAppRevIsNew(ctx) 112 if err != nil { 113 return err 114 } 115 if h.isNewRevision && needGenerateRevision { 116 h.currentAppRev.Name, _ = utils.GetAppNextRevision(h.app) 117 } 118 119 // MUST pass app revision name to appfile 120 // appfile depends it to render resources and do health checking 121 af.AppRevisionName = h.currentAppRev.Name 122 af.AppRevisionHash = h.currentRevHash 123 return nil 124 } 125 126 // gatherRevisionSpec will gather all revision spec without metadata and rendered result. 127 // the gathered Revision spec will be enough to calculate the hash and compare with the old revision 128 func (h *AppHandler) gatherRevisionSpec(af *appfile.Appfile) (*v1beta1.ApplicationRevision, string, error) { 129 copiedApp := h.app.DeepCopy() 130 // We better to remove all object status in the appRevision 131 copiedApp.Status = common.AppStatus{} 132 appRev := &v1beta1.ApplicationRevision{ 133 Spec: v1beta1.ApplicationRevisionSpec{ 134 ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{ 135 Application: *copiedApp, 136 ComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition), 137 WorkloadDefinitions: make(map[string]v1beta1.WorkloadDefinition), 138 TraitDefinitions: make(map[string]*v1beta1.TraitDefinition), 139 PolicyDefinitions: make(map[string]v1beta1.PolicyDefinition), 140 WorkflowStepDefinitions: make(map[string]*v1beta1.WorkflowStepDefinition), 141 Policies: make(map[string]v1alpha1.Policy), 142 }, 143 }, 144 } 145 for _, w := range af.ParsedComponents { 146 if w == nil { 147 continue 148 } 149 if w.FullTemplate.ComponentDefinition != nil { 150 cd := w.FullTemplate.ComponentDefinition.DeepCopy() 151 cd.Status = v1beta1.ComponentDefinitionStatus{} 152 appRev.Spec.ComponentDefinitions[w.FullTemplate.ComponentDefinition.Name] = cd.DeepCopy() 153 } 154 if w.FullTemplate.WorkloadDefinition != nil { 155 wd := w.FullTemplate.WorkloadDefinition.DeepCopy() 156 wd.Status = v1beta1.WorkloadDefinitionStatus{} 157 appRev.Spec.WorkloadDefinitions[w.FullTemplate.WorkloadDefinition.Name] = *wd 158 } 159 for _, t := range w.Traits { 160 if t == nil { 161 continue 162 } 163 if t.FullTemplate.TraitDefinition != nil { 164 td := t.FullTemplate.TraitDefinition.DeepCopy() 165 td.Status = v1beta1.TraitDefinitionStatus{} 166 appRev.Spec.TraitDefinitions[t.FullTemplate.TraitDefinition.Name] = td.DeepCopy() 167 } 168 } 169 } 170 for _, p := range af.ParsedPolicies { 171 if p == nil || p.FullTemplate == nil { 172 continue 173 } 174 if p.FullTemplate.PolicyDefinition != nil { 175 pd := p.FullTemplate.PolicyDefinition.DeepCopy() 176 pd.Status = v1beta1.PolicyDefinitionStatus{} 177 appRev.Spec.PolicyDefinitions[p.FullTemplate.PolicyDefinition.Name] = *pd 178 } 179 } 180 for name, def := range af.RelatedComponentDefinitions { 181 appRev.Spec.ComponentDefinitions[name] = def.DeepCopy() 182 } 183 for name, def := range af.RelatedTraitDefinitions { 184 appRev.Spec.TraitDefinitions[name] = def.DeepCopy() 185 } 186 for name, def := range af.RelatedWorkflowStepDefinitions { 187 appRev.Spec.WorkflowStepDefinitions[name] = def.DeepCopy() 188 } 189 for name, po := range af.ExternalPolicies { 190 appRev.Spec.Policies[name] = *po 191 } 192 var err error 193 if appRev.Spec.ReferredObjects, err = component.ConvertUnstructuredsToReferredObjects(af.ReferredObjects); err != nil { 194 return nil, "", errors.Wrapf(err, "failed to marshal referred object") 195 } 196 appRev.Spec.Workflow = af.ExternalWorkflow 197 198 appRevisionHash, err := ComputeAppRevisionHash(appRev) 199 if err != nil { 200 klog.ErrorS(err, "Failed to compute hash of appRevision for application", "application", klog.KObj(h.app)) 201 return appRev, "", errors.Wrapf(err, "failed to compute app revision hash") 202 } 203 return appRev, appRevisionHash, nil 204 } 205 206 func (h *AppHandler) getLatestAppRevision(ctx context.Context) error { 207 if DisableAllApplicationRevision { 208 return nil 209 } 210 if h.app.Status.LatestRevision == nil || len(h.app.Status.LatestRevision.Name) == 0 { 211 return nil 212 } 213 latestRevName := h.app.Status.LatestRevision.Name 214 latestAppRev := &v1beta1.ApplicationRevision{} 215 if err := h.Get(ctx, client.ObjectKey{Name: latestRevName, Namespace: h.app.Namespace}, latestAppRev); err != nil { 216 klog.ErrorS(err, "Failed to get latest app revision", "appRevisionName", latestRevName) 217 return errors.Wrapf(err, "fail to get latest app revision %s", latestRevName) 218 } 219 h.latestAppRev = latestAppRev 220 return nil 221 } 222 223 // ComputeAppRevisionHash computes a single hash value for an appRevision object 224 // Spec of Application/WorkloadDefinitions/ComponentDefinitions/TraitDefinitions/ScopeDefinitions will be taken into compute 225 func ComputeAppRevisionHash(appRevision *v1beta1.ApplicationRevision) (string, error) { 226 // Calculate Hash for New Mode with workflow and policy 227 revHash := struct { 228 ApplicationSpecHash string 229 WorkloadDefinitionHash map[string]string 230 ComponentDefinitionHash map[string]string 231 TraitDefinitionHash map[string]string 232 ScopeDefinitionHash map[string]string 233 PolicyDefinitionHash map[string]string 234 WorkflowStepDefinitionHash map[string]string 235 PolicyHash map[string]string 236 WorkflowHash string 237 ReferredObjectsHash string 238 }{ 239 WorkloadDefinitionHash: make(map[string]string), 240 ComponentDefinitionHash: make(map[string]string), 241 TraitDefinitionHash: make(map[string]string), 242 ScopeDefinitionHash: make(map[string]string), 243 PolicyDefinitionHash: make(map[string]string), 244 WorkflowStepDefinitionHash: make(map[string]string), 245 PolicyHash: make(map[string]string), 246 } 247 var err error 248 revHash.ApplicationSpecHash, err = utils.ComputeSpecHash(appRevision.Spec.Application.Spec) 249 if err != nil { 250 return "", err 251 } 252 for key, wd := range appRevision.Spec.WorkloadDefinitions { 253 wdCopy := wd 254 hash, err := utils.ComputeSpecHash(&wdCopy.Spec) 255 if err != nil { 256 return "", err 257 } 258 revHash.WorkloadDefinitionHash[key] = hash 259 } 260 for key, cd := range appRevision.Spec.ComponentDefinitions { 261 hash, err := utils.ComputeSpecHash(&cd.Spec) 262 if err != nil { 263 return "", err 264 } 265 revHash.ComponentDefinitionHash[key] = hash 266 } 267 for key, td := range appRevision.Spec.TraitDefinitions { 268 hash, err := utils.ComputeSpecHash(&td.Spec) 269 if err != nil { 270 return "", err 271 } 272 revHash.TraitDefinitionHash[key] = hash 273 } 274 for key, pd := range appRevision.Spec.PolicyDefinitions { 275 pdCopy := pd 276 hash, err := utils.ComputeSpecHash(&pdCopy.Spec) 277 if err != nil { 278 return "", err 279 } 280 revHash.PolicyDefinitionHash[key] = hash 281 } 282 for key, wd := range appRevision.Spec.WorkflowStepDefinitions { 283 hash, err := utils.ComputeSpecHash(&wd.Spec) 284 if err != nil { 285 return "", err 286 } 287 revHash.WorkflowStepDefinitionHash[key] = hash 288 } 289 for key, po := range appRevision.Spec.Policies { 290 hash, err := utils.ComputeSpecHash(po.Properties) 291 if err != nil { 292 return "", err 293 } 294 revHash.PolicyHash[key] = hash + po.Type 295 } 296 if appRevision.Spec.Workflow != nil { 297 revHash.WorkflowHash, err = utils.ComputeSpecHash(appRevision.Spec.Workflow.Steps) 298 if err != nil { 299 return "", err 300 } 301 } 302 revHash.ReferredObjectsHash, err = utils.ComputeSpecHash(appRevision.Spec.ReferredObjects) 303 if err != nil { 304 return "", err 305 } 306 return utils.ComputeSpecHash(&revHash) 307 } 308 309 // currentAppRevIsNew check application revision already exist or not 310 func (h *AppHandler) currentAppRevIsNew(ctx context.Context) (bool, bool, error) { 311 // the last revision doesn't exist. 312 if h.app.Status.LatestRevision == nil || DisableAllApplicationRevision { 313 return true, true, nil 314 } 315 316 isLatestRev := deepEqualAppInRevision(h.latestAppRev, h.currentAppRev) 317 if metav1.HasAnnotation(h.app.ObjectMeta, oam.AnnotationAutoUpdate) { 318 isLatestRev = h.app.Status.LatestRevision.RevisionHash == h.currentRevHash && DeepEqualRevision(h.latestAppRev, h.currentAppRev) 319 } 320 if h.latestAppRev != nil && oam.GetPublishVersion(h.app) != oam.GetPublishVersion(h.latestAppRev) { 321 isLatestRev = false 322 } 323 324 // diff the latest revision first 325 if isLatestRev { 326 appSpec := h.currentAppRev.Spec.Application.Spec 327 traitDef := h.currentAppRev.Spec.TraitDefinitions 328 workflowStepDef := h.currentAppRev.Spec.WorkflowStepDefinitions 329 h.currentAppRev = h.latestAppRev.DeepCopy() 330 h.currentRevHash = h.app.Status.LatestRevision.RevisionHash 331 h.currentAppRev.Spec.Application.Spec = appSpec 332 h.currentAppRev.Spec.TraitDefinitions = traitDef 333 h.currentAppRev.Spec.WorkflowStepDefinitions = workflowStepDef 334 return false, false, nil 335 } 336 337 revs, err := GetAppRevisions(ctx, h.Client, h.app.Name, h.app.Namespace) 338 if err != nil { 339 klog.ErrorS(err, "Failed to list app revision", "appName", h.app.Name) 340 return false, false, errors.Wrap(err, "failed to list app revision") 341 } 342 343 for _, _rev := range revs { 344 rev := _rev.DeepCopy() 345 if rev.GetLabels()[oam.LabelAppRevisionHash] == h.currentRevHash && 346 DeepEqualRevision(rev, h.currentAppRev) && 347 oam.GetPublishVersion(rev) == oam.GetPublishVersion(h.app) { 348 // we set currentAppRev to existRevision 349 h.currentAppRev = rev 350 return true, false, nil 351 } 352 } 353 354 // if reach here, it has different spec 355 return true, true, nil 356 } 357 358 // DeepEqualRevision will compare the spec of Application and Definition to see if the Application is the same revision 359 // Spec of AC and Component will not be compared as they are generated by the application and definitions 360 // Note the Spec compare can only work when the RawExtension are decoded well in the RawExtension.Object instead of in RawExtension.Raw(bytes) 361 func DeepEqualRevision(old, new *v1beta1.ApplicationRevision) bool { 362 if len(old.Spec.WorkloadDefinitions) != len(new.Spec.WorkloadDefinitions) { 363 return false 364 } 365 oldTraitDefinitions := old.Spec.TraitDefinitions 366 newTraitDefinitions := new.Spec.TraitDefinitions 367 if len(oldTraitDefinitions) != len(newTraitDefinitions) { 368 return false 369 } 370 if len(old.Spec.ComponentDefinitions) != len(new.Spec.ComponentDefinitions) { 371 return false 372 } 373 for key, wd := range new.Spec.WorkloadDefinitions { 374 if !apiequality.Semantic.DeepEqual(old.Spec.WorkloadDefinitions[key].Spec, wd.Spec) { 375 return false 376 } 377 } 378 for key, cd := range new.Spec.ComponentDefinitions { 379 if !apiequality.Semantic.DeepEqual(old.Spec.ComponentDefinitions[key].Spec, cd.Spec) { 380 return false 381 } 382 } 383 for key, td := range newTraitDefinitions { 384 if !apiequality.Semantic.DeepEqual(oldTraitDefinitions[key].Spec, td.Spec) { 385 return false 386 } 387 } 388 return deepEqualAppInRevision(old, new) 389 } 390 391 func deepEqualPolicy(old, new v1alpha1.Policy) bool { 392 return old.Type == new.Type && apiequality.Semantic.DeepEqual(old.Properties, new.Properties) 393 } 394 395 func deepEqualWorkflow(old, new workflowv1alpha1.Workflow) bool { 396 return apiequality.Semantic.DeepEqual(old.Steps, new.Steps) 397 } 398 399 const velaVersionNumberToCompareWorkflow = "v1.5.7" 400 401 func deepEqualAppSpec(old, new *v1beta1.Application) bool { 402 oldSpec, newSpec := old.Spec.DeepCopy(), new.Spec.DeepCopy() 403 // legacy code: KubeVela version before v1.5.7 & v1.6.0-alpha.4 does not 404 // record workflow in application spec in application revision. The comparison 405 // need to bypass the equality check of workflow to prevent unintended rerun 406 curVerNum := k8s.GetAnnotation(old, oam.AnnotationKubeVelaVersion) 407 publishVersion := k8s.GetAnnotation(old, oam.AnnotationPublishVersion) 408 if publishVersion == "" && curVerNum != "" { 409 cmpVer, _ := version.NewVersion(velaVersionNumberToCompareWorkflow) 410 if curVer, err := version.NewVersion(curVerNum); err == nil && curVer.LessThan(cmpVer) { 411 oldSpec.Workflow = nil 412 newSpec.Workflow = nil 413 } 414 } 415 return apiequality.Semantic.DeepEqual(oldSpec, newSpec) 416 } 417 418 func deepEqualAppInRevision(old, new *v1beta1.ApplicationRevision) bool { 419 if len(old.Spec.Policies) != len(new.Spec.Policies) { 420 return false 421 } 422 for key, po := range new.Spec.Policies { 423 if !deepEqualPolicy(old.Spec.Policies[key], po) { 424 return false 425 } 426 } 427 if (old.Spec.Workflow == nil) != (new.Spec.Workflow == nil) { 428 return false 429 } 430 if old.Spec.Workflow != nil && new.Spec.Workflow != nil { 431 if !deepEqualWorkflow(*old.Spec.Workflow, *new.Spec.Workflow) { 432 return false 433 } 434 } 435 return deepEqualAppSpec(&old.Spec.Application, &new.Spec.Application) 436 } 437 438 // FinalizeAndApplyAppRevision finalise AppRevision object and apply it 439 func (h *AppHandler) FinalizeAndApplyAppRevision(ctx context.Context) error { 440 if DisableAllApplicationRevision { 441 return nil 442 } 443 444 if ctx, ok := ctx.(monitorContext.Context); ok { 445 subCtx := ctx.Fork("apply-app-revision", monitorContext.DurationMetric(func(v float64) { 446 metrics.AppReconcileStageDurationHistogram.WithLabelValues("apply-apprev").Observe(v) 447 })) 448 defer subCtx.Commit("finish apply app revision") 449 } 450 appRev := h.currentAppRev 451 appRev.Namespace = h.app.Namespace 452 appRev.SetGroupVersionKind(v1beta1.ApplicationRevisionGroupVersionKind) 453 // pass application's annotations & labels to app revision 454 appRev.SetAnnotations(h.app.GetAnnotations()) 455 delete(appRev.Annotations, oam.AnnotationLastAppliedConfiguration) 456 appRev.SetLabels(h.app.GetLabels()) 457 util.AddLabels(appRev, map[string]string{ 458 oam.LabelAppName: h.app.GetName(), 459 oam.LabelAppRevisionHash: h.currentRevHash, 460 }) 461 // ApplicationRevision must use Application as ctrl-owner 462 appRev.SetOwnerReferences([]metav1.OwnerReference{{ 463 APIVersion: v1beta1.SchemeGroupVersion.String(), 464 Kind: v1beta1.ApplicationKind, 465 Name: h.app.Name, 466 UID: h.app.UID, 467 Controller: pointer.Bool(true), 468 }}) 469 sharding.PropagateScheduledShardIDLabel(h.app, appRev) 470 471 gotAppRev := &v1beta1.ApplicationRevision{} 472 if err := h.Get(ctx, client.ObjectKey{Name: appRev.Name, Namespace: appRev.Namespace}, gotAppRev); err != nil { 473 if apierrors.IsNotFound(err) { 474 return h.Create(ctx, appRev) 475 } 476 return err 477 } 478 if apiequality.Semantic.DeepEqual(gotAppRev.Spec, appRev.Spec) && 479 apiequality.Semantic.DeepEqual(gotAppRev.GetLabels(), appRev.GetLabels()) && 480 apiequality.Semantic.DeepEqual(gotAppRev.GetAnnotations(), appRev.GetAnnotations()) { 481 return nil 482 } 483 appRev.ResourceVersion = gotAppRev.ResourceVersion 484 485 // Set compression types (if enabled) 486 if utilfeature.DefaultMutableFeatureGate.Enabled(features.GzipApplicationRevision) { 487 appRev.Spec.Compression.SetType(compression.Gzip) 488 } 489 if utilfeature.DefaultMutableFeatureGate.Enabled(features.ZstdApplicationRevision) { 490 appRev.Spec.Compression.SetType(compression.Zstd) 491 } 492 493 return h.Update(ctx, appRev) 494 } 495 496 // UpdateAppLatestRevisionStatus only call to update app's latest revision status after applying manifests successfully 497 // otherwise it will override previous revision which is used during applying to do GC jobs 498 func (h *AppHandler) UpdateAppLatestRevisionStatus(ctx context.Context, patchStatus statusPatcher) error { 499 if DisableAllApplicationRevision { 500 return nil 501 } 502 if !h.isNewRevision { 503 // skip update if app revision is not changed 504 return nil 505 } 506 if ctx, ok := ctx.(monitorContext.Context); ok { 507 subCtx := ctx.Fork("update-apprev-status", monitorContext.DurationMetric(func(v float64) { 508 metrics.AppReconcileStageDurationHistogram.WithLabelValues("update-apprev-status").Observe(v) 509 })) 510 defer subCtx.Commit("application revision status updated") 511 } 512 revName := h.currentAppRev.Name 513 revNum, _ := util.ExtractRevisionNum(revName, "-") 514 h.app.Status.LatestRevision = &common.Revision{ 515 Name: h.currentAppRev.Name, 516 Revision: int64(revNum), 517 RevisionHash: h.currentRevHash, 518 } 519 if err := patchStatus(ctx, h.app, common.ApplicationRendering); err != nil { 520 klog.InfoS("Failed to update the latest appConfig revision to status", "application", klog.KObj(h.app), 521 "latest revision", revName, "err", err) 522 return err 523 } 524 klog.InfoS("Successfully update application latest revision status", "application", klog.KObj(h.app), 525 "latest revision", revName) 526 return nil 527 } 528 529 // UpdateApplicationRevisionStatus update application revision status 530 func (h *AppHandler) UpdateApplicationRevisionStatus(ctx context.Context, appRev *v1beta1.ApplicationRevision, wfStatus *common.WorkflowStatus) { 531 if appRev == nil || DisableAllApplicationRevision { 532 return 533 } 534 appRev.Status.Succeeded = wfStatus.Phase == workflowv1alpha1.WorkflowStateSucceeded 535 appRev.Status.Workflow = wfStatus 536 537 // Versioned the context backend values. 538 if wfStatus.ContextBackend != nil { 539 var cm corev1.ConfigMap 540 if err := h.Client.Get(ctx, ktypes.NamespacedName{Namespace: wfStatus.ContextBackend.Namespace, Name: wfStatus.ContextBackend.Name}, &cm); err != nil { 541 klog.Error(err, "[UpdateApplicationRevisionStatus] failed to load the context values", "ApplicationRevision", appRev.Name) 542 } 543 appRev.Status.WorkflowContext = cm.Data 544 } 545 546 if err := h.Client.Status().Update(ctx, appRev); err != nil { 547 if logCtx, ok := ctx.(monitorContext.Context); ok { 548 logCtx.Error(err, "[UpdateApplicationRevisionStatus] failed to update application revision status", "ApplicationRevision", appRev.Name) 549 } else { 550 klog.Error(err, "[UpdateApplicationRevisionStatus] failed to update application revision status", "ApplicationRevision", appRev.Name) 551 } 552 } 553 } 554 555 // GetAppRevisions get application revisions by label 556 func GetAppRevisions(ctx context.Context, cli client.Client, appName string, appNs string) ([]v1beta1.ApplicationRevision, error) { 557 appRevisionList := new(v1beta1.ApplicationRevisionList) 558 var err error 559 if cache.OptimizeListOp { 560 err = cli.List(ctx, appRevisionList, client.MatchingFields{cache.AppIndex: appNs + "/" + appName}) 561 } else { 562 err = cli.List(ctx, appRevisionList, client.InNamespace(appNs), client.MatchingLabels{oam.LabelAppName: appName}) 563 } 564 if err != nil { 565 return nil, err 566 } 567 return appRevisionList.Items, nil 568 } 569 570 // GetSortedAppRevisions get application revisions by revision number 571 func GetSortedAppRevisions(ctx context.Context, cli client.Client, appName string, appNs string) ([]v1beta1.ApplicationRevision, error) { 572 revs, err := GetAppRevisions(ctx, cli, appName, appNs) 573 if err != nil { 574 return nil, err 575 } 576 sort.Slice(revs, func(i, j int) bool { 577 ir, _ := util.ExtractRevisionNum(revs[i].Name, "-") 578 ij, _ := util.ExtractRevisionNum(revs[j].Name, "-") 579 return ir < ij 580 }) 581 return revs, nil 582 }