github.com/argoproj/argo-cd@v1.8.7/controller/state.go (about) 1 package controller 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "time" 8 9 "github.com/argoproj/gitops-engine/pkg/diff" 10 "github.com/argoproj/gitops-engine/pkg/health" 11 "github.com/argoproj/gitops-engine/pkg/sync" 12 hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook" 13 "github.com/argoproj/gitops-engine/pkg/sync/ignore" 14 resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource" 15 kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube" 16 log "github.com/sirupsen/logrus" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 19 "k8s.io/apimachinery/pkg/runtime/schema" 20 "k8s.io/apimachinery/pkg/types" 21 "k8s.io/client-go/tools/cache" 22 23 "github.com/argoproj/argo-cd/common" 24 statecache "github.com/argoproj/argo-cd/controller/cache" 25 "github.com/argoproj/argo-cd/controller/metrics" 26 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 27 appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 28 appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned" 29 "github.com/argoproj/argo-cd/reposerver/apiclient" 30 "github.com/argoproj/argo-cd/util/argo" 31 "github.com/argoproj/argo-cd/util/db" 32 "github.com/argoproj/argo-cd/util/gpg" 33 argohealth "github.com/argoproj/argo-cd/util/health" 34 "github.com/argoproj/argo-cd/util/io" 35 "github.com/argoproj/argo-cd/util/settings" 36 "github.com/argoproj/argo-cd/util/stats" 37 ) 38 39 type resourceInfoProviderStub struct { 40 } 41 42 func (r *resourceInfoProviderStub) IsNamespaced(_ schema.GroupKind) (bool, error) { 43 return false, nil 44 } 45 46 type managedResource struct { 47 Target *unstructured.Unstructured 48 Live *unstructured.Unstructured 49 Diff diff.DiffResult 50 Group string 51 Version string 52 Kind string 53 Namespace string 54 Name string 55 Hook bool 56 } 57 58 func GetLiveObjsForApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus) ([]*appv1.ResourceStatus, []*unstructured.Unstructured) { 59 liveObjs := make([]*unstructured.Unstructured, 0) 60 resStatuses := make([]*appv1.ResourceStatus, 0) 61 for i, resource := range resources { 62 if resource.Target != nil && hookutil.Skip(resource.Target) { 63 continue 64 } 65 66 liveObjs = append(liveObjs, resource.Live) 67 resStatuses = append(resStatuses, &statuses[i]) 68 } 69 return resStatuses, liveObjs 70 } 71 72 // AppStateManager defines methods which allow to compare application spec and actual application state. 73 type AppStateManager interface { 74 CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, localObjects []string) *comparisonResult 75 SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) 76 } 77 78 type comparisonResult struct { 79 syncStatus *v1alpha1.SyncStatus 80 healthStatus *v1alpha1.HealthStatus 81 resources []v1alpha1.ResourceStatus 82 managedResources []managedResource 83 reconciliationResult sync.ReconciliationResult 84 diffNormalizer diff.Normalizer 85 appSourceType v1alpha1.ApplicationSourceType 86 // timings maps phases of comparison to the duration it took to complete (for statistical purposes) 87 timings map[string]time.Duration 88 } 89 90 func (res *comparisonResult) GetSyncStatus() *v1alpha1.SyncStatus { 91 return res.syncStatus 92 } 93 94 func (res *comparisonResult) GetHealthStatus() *v1alpha1.HealthStatus { 95 return res.healthStatus 96 } 97 98 // appStateManager allows to compare applications to git 99 type appStateManager struct { 100 metricsServer *metrics.MetricsServer 101 db db.ArgoDB 102 settingsMgr *settings.SettingsManager 103 appclientset appclientset.Interface 104 projInformer cache.SharedIndexInformer 105 kubectl kubeutil.Kubectl 106 repoClientset apiclient.Clientset 107 liveStateCache statecache.LiveStateCache 108 namespace string 109 } 110 111 func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache, verifySignature bool) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) { 112 ts := stats.NewTimingStats() 113 helmRepos, err := m.db.ListHelmRepositories(context.Background()) 114 if err != nil { 115 return nil, nil, err 116 } 117 ts.AddCheckpoint("helm_ms") 118 repo, err := m.db.GetRepository(context.Background(), source.RepoURL) 119 if err != nil { 120 return nil, nil, err 121 } 122 ts.AddCheckpoint("repo_ms") 123 conn, repoClient, err := m.repoClientset.NewRepoServerClient() 124 if err != nil { 125 return nil, nil, err 126 } 127 defer io.Close(conn) 128 129 if revision == "" { 130 revision = source.TargetRevision 131 } 132 133 plugins, err := m.settingsMgr.GetConfigManagementPlugins() 134 if err != nil { 135 return nil, nil, err 136 } 137 ts.AddCheckpoint("plugins_ms") 138 tools := make([]*appv1.ConfigManagementPlugin, len(plugins)) 139 for i := range plugins { 140 tools[i] = &plugins[i] 141 } 142 143 kustomizeSettings, err := m.settingsMgr.GetKustomizeSettings() 144 if err != nil { 145 return nil, nil, err 146 } 147 kustomizeOptions, err := kustomizeSettings.GetOptions(app.Spec.Source) 148 if err != nil { 149 return nil, nil, err 150 } 151 ts.AddCheckpoint("build_options_ms") 152 serverVersion, apiGroups, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server) 153 if err != nil { 154 return nil, nil, err 155 } 156 ts.AddCheckpoint("version_ms") 157 manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ 158 Repo: repo, 159 Repos: helmRepos, 160 Revision: revision, 161 NoCache: noCache, 162 AppLabelKey: appLabelKey, 163 AppLabelValue: app.Name, 164 Namespace: app.Spec.Destination.Namespace, 165 ApplicationSource: &source, 166 Plugins: tools, 167 KustomizeOptions: kustomizeOptions, 168 KubeVersion: serverVersion, 169 ApiVersions: argo.APIGroupsToVersions(apiGroups), 170 VerifySignature: verifySignature, 171 }) 172 if err != nil { 173 return nil, nil, err 174 } 175 targetObjs, err := unmarshalManifests(manifestInfo.Manifests) 176 177 if err != nil { 178 return nil, nil, err 179 } 180 181 ts.AddCheckpoint("unmarshal_ms") 182 logCtx := log.WithField("application", app.Name) 183 for k, v := range ts.Timings() { 184 logCtx = logCtx.WithField(k, v.Milliseconds()) 185 } 186 logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) 187 logCtx.Info("getRepoObjs stats") 188 return targetObjs, manifestInfo, nil 189 } 190 191 func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) { 192 targetObjs := make([]*unstructured.Unstructured, 0) 193 for _, manifest := range manifests { 194 obj, err := v1alpha1.UnmarshalToUnstructured(manifest) 195 if err != nil { 196 return nil, err 197 } 198 targetObjs = append(targetObjs, obj) 199 } 200 return targetObjs, nil 201 } 202 203 func DeduplicateTargetObjects( 204 namespace string, 205 objs []*unstructured.Unstructured, 206 infoProvider kubeutil.ResourceInfoProvider, 207 ) ([]*unstructured.Unstructured, []v1alpha1.ApplicationCondition, error) { 208 209 targetByKey := make(map[kubeutil.ResourceKey][]*unstructured.Unstructured) 210 for i := range objs { 211 obj := objs[i] 212 if obj == nil { 213 continue 214 } 215 isNamespaced := kubeutil.IsNamespacedOrUnknown(infoProvider, obj.GroupVersionKind().GroupKind()) 216 if !isNamespaced { 217 obj.SetNamespace("") 218 } else if obj.GetNamespace() == "" { 219 obj.SetNamespace(namespace) 220 } 221 key := kubeutil.GetResourceKey(obj) 222 if key.Name == "" && obj.GetGenerateName() != "" { 223 key.Name = fmt.Sprintf("%s%d", obj.GetGenerateName(), i) 224 } 225 targetByKey[key] = append(targetByKey[key], obj) 226 } 227 conditions := make([]v1alpha1.ApplicationCondition, 0) 228 result := make([]*unstructured.Unstructured, 0) 229 for key, targets := range targetByKey { 230 if len(targets) > 1 { 231 now := metav1.Now() 232 conditions = append(conditions, appv1.ApplicationCondition{ 233 Type: appv1.ApplicationConditionRepeatedResourceWarning, 234 Message: fmt.Sprintf("Resource %s appeared %d times among application resources.", key.String(), len(targets)), 235 LastTransitionTime: &now, 236 }) 237 } 238 result = append(result, targets[len(targets)-1]) 239 } 240 241 return result, conditions, nil 242 } 243 244 func (m *appStateManager) getComparisonSettings(app *appv1.Application) (string, map[string]v1alpha1.ResourceOverride, diff.Normalizer, *settings.ResourcesFilter, error) { 245 resourceOverrides, err := m.settingsMgr.GetResourceOverrides() 246 if err != nil { 247 return "", nil, nil, nil, err 248 } 249 appLabelKey, err := m.settingsMgr.GetAppInstanceLabelKey() 250 if err != nil { 251 return "", nil, nil, nil, err 252 } 253 diffNormalizer, err := argo.NewDiffNormalizer(app.Spec.IgnoreDifferences, resourceOverrides) 254 if err != nil { 255 return "", nil, nil, nil, err 256 } 257 resFilter, err := m.settingsMgr.GetResourcesFilter() 258 if err != nil { 259 return "", nil, nil, nil, err 260 } 261 return appLabelKey, resourceOverrides, diffNormalizer, resFilter, nil 262 } 263 264 // verifyGnuPGSignature verifies the result of a GnuPG operation for a given git 265 // revision. 266 func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestInfo *apiclient.ManifestResponse) []appv1.ApplicationCondition { 267 now := metav1.Now() 268 conditions := make([]appv1.ApplicationCondition, 0) 269 // We need to have some data in the verification result to parse, otherwise there was no signature 270 if manifestInfo.VerifyResult != "" { 271 verifyResult, err := gpg.ParseGitCommitVerification(manifestInfo.VerifyResult) 272 if err != nil { 273 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 274 log.Errorf("Error while verifying git commit for revision %s: %s", revision, err.Error()) 275 } else { 276 switch verifyResult.Result { 277 case gpg.VerifyResultGood: 278 // This is the only case we allow to sync to, but we need to make sure signing key is allowed 279 validKey := false 280 for _, k := range project.Spec.SignatureKeys { 281 if gpg.KeyID(k.KeyID) == gpg.KeyID(verifyResult.KeyID) && gpg.KeyID(k.KeyID) != "" { 282 validKey = true 283 break 284 } 285 } 286 if !validKey { 287 msg := fmt.Sprintf("Found good signature made with %s key %s, but this key is not allowed in AppProject", 288 verifyResult.Cipher, verifyResult.KeyID) 289 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) 290 } 291 case gpg.VerifyResultInvalid: 292 msg := fmt.Sprintf("Found signature made with %s key %s, but verification result was invalid: '%s'", 293 verifyResult.Cipher, verifyResult.KeyID, verifyResult.Message) 294 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) 295 default: 296 msg := fmt.Sprintf("Could not verify commit signature on revision '%s', check logs for more information.", revision) 297 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) 298 } 299 } 300 } else { 301 msg := fmt.Sprintf("Target revision %s in Git is not signed, but a signature is required", revision) 302 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) 303 } 304 305 return conditions 306 } 307 308 // CompareAppState compares application git state to the live app state, using the specified 309 // revision and supplied source. If revision or overrides are empty, then compares against 310 // revision and overrides in the app spec. 311 func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, localManifests []string) *comparisonResult { 312 ts := stats.NewTimingStats() 313 appLabelKey, resourceOverrides, diffNormalizer, resFilter, err := m.getComparisonSettings(app) 314 ts.AddCheckpoint("settings_ms") 315 316 // return unknown comparison result if basic comparison settings cannot be loaded 317 if err != nil { 318 return &comparisonResult{ 319 syncStatus: &v1alpha1.SyncStatus{ 320 ComparedTo: appv1.ComparedTo{Source: source, Destination: app.Spec.Destination}, 321 Status: appv1.SyncStatusCodeUnknown, 322 }, 323 healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown}, 324 } 325 } 326 327 // When signature keys are defined in the project spec, we need to verify the signature on the Git revision 328 verifySignature := false 329 if project.Spec.SignatureKeys != nil && len(project.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled() { 330 verifySignature = true 331 } 332 333 // do best effort loading live and target state to present as much information about app state as possible 334 failedToLoadObjs := false 335 conditions := make([]v1alpha1.ApplicationCondition, 0) 336 337 logCtx := log.WithField("application", app.Name) 338 logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace) 339 340 var targetObjs []*unstructured.Unstructured 341 var manifestInfo *apiclient.ManifestResponse 342 now := metav1.Now() 343 344 if len(localManifests) == 0 { 345 targetObjs, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache, verifySignature) 346 if err != nil { 347 targetObjs = make([]*unstructured.Unstructured, 0) 348 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 349 failedToLoadObjs = true 350 } 351 } else { 352 // Prevent applying local manifests for now when signature verification is enabled 353 // This is also enforced on API level, but as a last resort, we also enforce it here 354 if gpg.IsGPGEnabled() && verifySignature { 355 msg := "Cannot use local manifests when signature verification is required" 356 targetObjs = make([]*unstructured.Unstructured, 0) 357 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) 358 failedToLoadObjs = true 359 } else { 360 targetObjs, err = unmarshalManifests(localManifests) 361 if err != nil { 362 targetObjs = make([]*unstructured.Unstructured, 0) 363 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 364 failedToLoadObjs = true 365 } 366 } 367 manifestInfo = nil 368 } 369 ts.AddCheckpoint("git_ms") 370 371 var infoProvider kubeutil.ResourceInfoProvider 372 infoProvider, err = m.liveStateCache.GetClusterCache(app.Spec.Destination.Server) 373 if err != nil { 374 infoProvider = &resourceInfoProviderStub{} 375 } 376 targetObjs, dedupConditions, err := DeduplicateTargetObjects(app.Spec.Destination.Namespace, targetObjs, infoProvider) 377 if err != nil { 378 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 379 } 380 conditions = append(conditions, dedupConditions...) 381 for i := len(targetObjs) - 1; i >= 0; i-- { 382 targetObj := targetObjs[i] 383 gvk := targetObj.GroupVersionKind() 384 if resFilter.IsExcludedResource(gvk.Group, gvk.Kind, app.Spec.Destination.Server) { 385 targetObjs = append(targetObjs[:i], targetObjs[i+1:]...) 386 conditions = append(conditions, v1alpha1.ApplicationCondition{ 387 Type: v1alpha1.ApplicationConditionExcludedResourceWarning, 388 Message: fmt.Sprintf("Resource %s/%s %s is excluded in the settings", gvk.Group, gvk.Kind, targetObj.GetName()), 389 LastTransitionTime: &now, 390 }) 391 } 392 } 393 ts.AddCheckpoint("dedup_ms") 394 395 liveObjByKey, err := m.liveStateCache.GetManagedLiveObjs(app, targetObjs) 396 if err != nil { 397 liveObjByKey = make(map[kubeutil.ResourceKey]*unstructured.Unstructured) 398 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 399 failedToLoadObjs = true 400 } 401 logCtx.Debugf("Retrieved lived manifests") 402 403 // filter out all resources which are not permitted in the application project 404 for k, v := range liveObjByKey { 405 if !project.IsLiveResourcePermitted(v, app.Spec.Destination.Server) { 406 delete(liveObjByKey, k) 407 } 408 } 409 410 for _, liveObj := range liveObjByKey { 411 if liveObj != nil { 412 appInstanceName := kubeutil.GetAppInstanceLabel(liveObj, appLabelKey) 413 if appInstanceName != "" && appInstanceName != app.Name { 414 conditions = append(conditions, v1alpha1.ApplicationCondition{ 415 Type: v1alpha1.ApplicationConditionSharedResourceWarning, 416 Message: fmt.Sprintf("%s/%s is part of applications %s and %s", liveObj.GetKind(), liveObj.GetName(), app.Name, appInstanceName), 417 LastTransitionTime: &now, 418 }) 419 } 420 } 421 } 422 423 reconciliation := sync.Reconcile(targetObjs, liveObjByKey, app.Spec.Destination.Namespace, infoProvider) 424 ts.AddCheckpoint("live_ms") 425 426 compareOptions, err := m.settingsMgr.GetResourceCompareOptions() 427 if err != nil { 428 log.Warnf("Could not get compare options from ConfigMap (assuming defaults): %v", err) 429 compareOptions = settings.GetDefaultDiffOptions() 430 } 431 432 logCtx.Debugf("built managed objects list") 433 // Do the actual comparison 434 diffResults, err := diff.DiffArray( 435 reconciliation.Target, reconciliation.Live, 436 diff.WithNormalizer(diffNormalizer), 437 diff.IgnoreAggregatedRoles(compareOptions.IgnoreAggregatedRoles)) 438 if err != nil { 439 diffResults = &diff.DiffResultList{} 440 failedToLoadObjs = true 441 conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 442 } 443 ts.AddCheckpoint("diff_ms") 444 445 syncCode := v1alpha1.SyncStatusCodeSynced 446 managedResources := make([]managedResource, len(reconciliation.Target)) 447 resourceSummaries := make([]v1alpha1.ResourceStatus, len(reconciliation.Target)) 448 for i, targetObj := range reconciliation.Target { 449 liveObj := reconciliation.Live[i] 450 obj := liveObj 451 if obj == nil { 452 obj = targetObj 453 } 454 if obj == nil { 455 continue 456 } 457 gvk := obj.GroupVersionKind() 458 459 resState := v1alpha1.ResourceStatus{ 460 Namespace: obj.GetNamespace(), 461 Name: obj.GetName(), 462 Kind: gvk.Kind, 463 Version: gvk.Version, 464 Group: gvk.Group, 465 Hook: hookutil.IsHook(obj), 466 RequiresPruning: targetObj == nil && liveObj != nil, 467 } 468 469 var diffResult diff.DiffResult 470 if i < len(diffResults.Diffs) { 471 diffResult = diffResults.Diffs[i] 472 } else { 473 diffResult = diff.DiffResult{Modified: false, NormalizedLive: []byte("{}"), PredictedLive: []byte("{}")} 474 } 475 if resState.Hook || ignore.Ignore(obj) || (targetObj != nil && hookutil.Skip(targetObj)) { 476 // For resource hooks or skipped resources, don't store sync status, and do not affect overall sync status 477 } else if diffResult.Modified || targetObj == nil || liveObj == nil { 478 // Set resource state to OutOfSync since one of the following is true: 479 // * target and live resource are different 480 // * target resource not defined and live resource is extra 481 // * target resource present but live resource is missing 482 resState.Status = v1alpha1.SyncStatusCodeOutOfSync 483 // we ignore the status if the obj needs pruning AND we have the annotation 484 needsPruning := targetObj == nil && liveObj != nil 485 if !(needsPruning && resourceutil.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreExtraneous")) { 486 syncCode = v1alpha1.SyncStatusCodeOutOfSync 487 } 488 } else { 489 resState.Status = v1alpha1.SyncStatusCodeSynced 490 } 491 // set unknown status to all resource that are not permitted in the app project 492 isNamespaced, err := m.liveStateCache.IsNamespaced(app.Spec.Destination.Server, gvk.GroupKind()) 493 if !project.IsGroupKindPermitted(gvk.GroupKind(), isNamespaced && err == nil) { 494 resState.Status = v1alpha1.SyncStatusCodeUnknown 495 } 496 497 if isNamespaced && obj.GetNamespace() == "" { 498 conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionInvalidSpecError, Message: fmt.Sprintf("Namespace for %s %s is missing.", obj.GetName(), gvk.String()), LastTransitionTime: &now}) 499 } 500 501 // we can't say anything about the status if we were unable to get the target objects 502 if failedToLoadObjs { 503 resState.Status = v1alpha1.SyncStatusCodeUnknown 504 } 505 managedResources[i] = managedResource{ 506 Name: resState.Name, 507 Namespace: resState.Namespace, 508 Group: resState.Group, 509 Kind: resState.Kind, 510 Version: resState.Version, 511 Live: liveObj, 512 Target: targetObj, 513 Diff: diffResult, 514 Hook: resState.Hook, 515 } 516 resourceSummaries[i] = resState 517 } 518 519 if failedToLoadObjs { 520 syncCode = v1alpha1.SyncStatusCodeUnknown 521 } 522 syncStatus := v1alpha1.SyncStatus{ 523 ComparedTo: appv1.ComparedTo{ 524 Source: source, 525 Destination: app.Spec.Destination, 526 }, 527 Status: syncCode, 528 } 529 if manifestInfo != nil { 530 syncStatus.Revision = manifestInfo.Revision 531 } 532 ts.AddCheckpoint("sync_ms") 533 534 resSumForAppHealth, liveObjsForAppHealth := GetLiveObjsForApplicationHealth(managedResources, resourceSummaries) 535 healthStatus, err := argohealth.SetApplicationHealth(resSumForAppHealth, liveObjsForAppHealth, resourceOverrides, func(obj *unstructured.Unstructured) bool { 536 return !isSelfReferencedApp(app, kubeutil.GetObjectRef(obj)) 537 }) 538 539 if err != nil { 540 conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) 541 } 542 543 // Git has already performed the signature verification via its GPG interface, and the result is available 544 // in the manifest info received from the repository server. We now need to form our opinion about the result 545 // and stop processing if we do not agree about the outcome. 546 if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil { 547 conditions = append(conditions, verifyGnuPGSignature(revision, project, manifestInfo)...) 548 } 549 550 compRes := comparisonResult{ 551 syncStatus: &syncStatus, 552 healthStatus: healthStatus, 553 resources: resourceSummaries, 554 managedResources: managedResources, 555 reconciliationResult: reconciliation, 556 diffNormalizer: diffNormalizer, 557 } 558 if manifestInfo != nil { 559 compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType) 560 } 561 app.Status.SetConditions(conditions, map[appv1.ApplicationConditionType]bool{ 562 appv1.ApplicationConditionComparisonError: true, 563 appv1.ApplicationConditionSharedResourceWarning: true, 564 appv1.ApplicationConditionRepeatedResourceWarning: true, 565 appv1.ApplicationConditionExcludedResourceWarning: true, 566 }) 567 ts.AddCheckpoint("health_ms") 568 compRes.timings = ts.Timings() 569 return &compRes 570 } 571 572 func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, startedAt metav1.Time) error { 573 var nextID int64 574 if len(app.Status.History) > 0 { 575 nextID = app.Status.History.LastRevisionHistory().ID + 1 576 } 577 app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{ 578 Revision: revision, 579 DeployedAt: metav1.NewTime(time.Now().UTC()), 580 DeployStartedAt: &startedAt, 581 ID: nextID, 582 Source: source, 583 }) 584 585 app.Status.History = app.Status.History.Trunc(app.Spec.GetRevisionHistoryLimit()) 586 587 patch, err := json.Marshal(map[string]map[string][]v1alpha1.RevisionHistory{ 588 "status": { 589 "history": app.Status.History, 590 }, 591 }) 592 if err != nil { 593 return err 594 } 595 _, err = m.appclientset.ArgoprojV1alpha1().Applications(m.namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) 596 return err 597 } 598 599 // NewAppStateManager creates new instance of AppStateManager 600 func NewAppStateManager( 601 db db.ArgoDB, 602 appclientset appclientset.Interface, 603 repoClientset apiclient.Clientset, 604 namespace string, 605 kubectl kubeutil.Kubectl, 606 settingsMgr *settings.SettingsManager, 607 liveStateCache statecache.LiveStateCache, 608 projInformer cache.SharedIndexInformer, 609 metricsServer *metrics.MetricsServer, 610 ) AppStateManager { 611 return &appStateManager{ 612 liveStateCache: liveStateCache, 613 db: db, 614 appclientset: appclientset, 615 kubectl: kubectl, 616 repoClientset: repoClientset, 617 namespace: namespace, 618 settingsMgr: settingsMgr, 619 projInformer: projInformer, 620 metricsServer: metricsServer, 621 } 622 }