Skip to content

Commit

Permalink
[DEVHAS-488]annotation to force generate gitops (#410)
Browse files Browse the repository at this point in the history
* add force annotation check

Signed-off-by: Stephanie <[email protected]>

* set annotation back to false once generation successfully completed

Signed-off-by: Stephanie <[email protected]>

* address review comments

Signed-off-by: Stephanie <[email protected]>

* update message and time

Signed-off-by: Stephanie <[email protected]>

* add test

Signed-off-by: Stephanie <[email protected]>

* clean up

Signed-off-by: Stephanie <[email protected]>

---------

Signed-off-by: Stephanie <[email protected]>
  • Loading branch information
yangcao77 authored Nov 15, 2023
1 parent bf72a7c commit c3e6afd
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 7 deletions.
4 changes: 2 additions & 2 deletions controllers/application_controller_finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func containsString(slice []string, s string) bool {
return false
}

// getApplicationFailCount gets the given counter annotation on the resource (defaults to 0 if unset)
// getCounterAnnotation gets the given counter annotation on the resource (defaults to 0 if unset)
func getCounterAnnotation(annotation string, obj client.Object) (int, error) {
objAnnotations := obj.GetAnnotations()
if objAnnotations == nil || objAnnotations[annotation] == "" {
Expand All @@ -100,7 +100,7 @@ func getCounterAnnotation(annotation string, obj client.Object) (int, error) {
return strconv.Atoi(counterAnnotation)
}

// setApplicationFailCount sets the given counter annotation on the resource to the specified value
// setCounterAnnotation sets the given counter annotation on the resource to the specified value
func setCounterAnnotation(annotation string, obj client.Object, count int) {
objAnnotations := obj.GetAnnotations()
if objAnnotations == nil {
Expand Down
25 changes: 23 additions & 2 deletions controllers/component_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const (
applicationFailCounterAnnotation = "applicationFailCounter"
maxApplicationFailCount = 5
componentName = "Component"
forceGenerationAnnotation = "forceGitopsGeneration"
)

//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=components,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -131,6 +132,8 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}

setCounterAnnotation(applicationFailCounterAnnotation, &component, 0)
forceGenerateGitopsResource := getForceGenerateGitopsAnnotation(component)
log.Info(fmt.Sprintf("forceGenerateGitopsResource is %v", forceGenerateGitopsResource))

ghClient, err := r.GitHubTokenClient.GetNewGitHubClient("")
if err != nil {
Expand Down Expand Up @@ -180,7 +183,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
log.Info(fmt.Sprintf("added the finalizer %v", req.NamespacedName))
}
} else {
if hasApplication.Status.Devfile != "" && len(component.Status.Conditions) > 0 && component.Status.Conditions[len(component.Status.Conditions)-1].Status == metav1.ConditionTrue && containsString(component.GetFinalizers(), compFinalizerName) {
if hasApplication.Status.Devfile != "" && (forceGenerateGitopsResource || len(component.Status.Conditions) > 0 && component.Status.Conditions[len(component.Status.Conditions)-1].Status == metav1.ConditionTrue && containsString(component.GetFinalizers(), compFinalizerName)) {
// only attempt to finalize and update the gitops repo if an Application is present & the previous Component status is good
// A finalizer is present for the Component CR, so make sure we do the necessary cleanup steps
metrics.ComponentDeletionTotalReqs.Inc()
Expand Down Expand Up @@ -217,7 +220,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
isUpdateConditionPresent := false
isGitOpsRegenSuccessful := false
for _, condition := range component.Status.Conditions {
if condition.Type == "GitOpsResourcesGenerated" && condition.Reason == "GenerateError" && condition.Status == metav1.ConditionFalse {
if forceGenerateGitopsResource || (condition.Type == "GitOpsResourcesGenerated" && condition.Reason == "GenerateError" && condition.Status == metav1.ConditionFalse) {
log.Info(fmt.Sprintf("Re-attempting GitOps generation for %s", component.Name))
// Parse the Component Devfile
compDevfileData, err := cdqanalysis.ParseDevfileWithParserArgs(&devfileParser.ParserArgs{Data: []byte(component.Status.Devfile), Token: gitToken})
Expand All @@ -238,6 +241,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
if err != nil {
return ctrl.Result{}, err
}
setForceGenerateGitopsAnnotation(&component, "false")
isGitOpsRegenSuccessful = true
}
} else if condition.Type == "Updated" && condition.Reason == "Error" && condition.Status == metav1.ConditionFalse {
Expand Down Expand Up @@ -754,3 +758,20 @@ func (r *ComponentReconciler) incrementCounterAndRequeue(log logr.Logger, ctx co
return ctrl.Result{}, componentErr
}
}

// getForceGenerateGitopsAnnotation gets the internal annotation on the component whether to force generate the gitops resource
func getForceGenerateGitopsAnnotation(component appstudiov1alpha1.Component) bool {
compAnnotations := component.GetAnnotations()
if compAnnotations != nil && compAnnotations[forceGenerationAnnotation] == "true" {
return true
}
return false
}

func setForceGenerateGitopsAnnotation(component *appstudiov1alpha1.Component, value string) {
compAnnotations := component.GetAnnotations()
if compAnnotations == nil {
component.Annotations = make(map[string]string)
}
component.Annotations[forceGenerationAnnotation] = value
}
10 changes: 7 additions & 3 deletions controllers/component_controller_conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,21 @@ func (r *ComponentReconciler) SetUpdateConditionAndUpdateCR(ctx context.Context,
func (r *ComponentReconciler) SetGitOpsGeneratedConditionAndUpdateCR(ctx context.Context, req ctrl.Request, component *appstudiov1alpha1.Component, generateError error) error {
log := ctrl.LoggerFrom(ctx)
condition := metav1.Condition{}

forceGenerateGitopsResource := getForceGenerateGitopsAnnotation(*component)
conditionType := "GitOpsResourcesGenerated"
if forceGenerateGitopsResource {
conditionType = "GitOpsResourcesForceGenerated"
}
if generateError == nil {
condition = metav1.Condition{
Type: "GitOpsResourcesGenerated",
Type: conditionType,
Status: metav1.ConditionTrue,
Reason: "OK",
Message: "GitOps resource generated successfully",
}
} else {
condition = metav1.Condition{
Type: "GitOpsResourcesGenerated",
Type: conditionType,
Status: metav1.ConditionFalse,
Reason: "GenerateError",
Message: fmt.Sprintf("GitOps resources failed to generate: %v", generateError),
Expand Down
76 changes: 76 additions & 0 deletions controllers/component_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2016,6 +2016,82 @@ var _ = Describe("Component controller", func() {
deleteHASAppCR(hasAppLookupKey)
})
})
Context("force generate gitops resource", func() {
It("Should successfully update CR conditions and status", func() {
ctx := context.Background()

applicationName := HASAppName + "23"
componentName := HASCompName + "23"

createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description)

hasComp := &appstudiov1alpha1.Component{
TypeMeta: metav1.TypeMeta{
APIVersion: "appstudio.redhat.com/v1alpha1",
Kind: "Component",
},
ObjectMeta: metav1.ObjectMeta{
Name: componentName,
Namespace: HASAppNamespace,
},
Spec: appstudiov1alpha1.ComponentSpec{
ComponentName: ComponentName,
Application: applicationName,
Source: appstudiov1alpha1.ComponentSource{
ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{
GitSource: &appstudiov1alpha1.GitSource{
URL: SampleRepoLink,
},
},
},
},
}
Expect(k8sClient.Create(ctx, hasComp)).Should(Succeed())

// Look up the has app resource that was created.
// num(conditions) may still be < 2 (GeneratedGitOps, Created) on the first try, so retry until at least _some_ condition is set
hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace}
createdHasComp := &appstudiov1alpha1.Component{}
Eventually(func() bool {
k8sClient.Get(context.Background(), hasCompLookupKey, createdHasComp)
return len(createdHasComp.Status.Conditions) > 1 && createdHasComp.Status.GitOps.RepositoryURL != ""
}, timeout, interval).Should(BeTrue())
// Verify that the GitOpsGenerated status condition was also set
gitopsCondition := createdHasComp.Status.Conditions[len(createdHasComp.Status.Conditions)-2]
Expect(gitopsCondition.Type).To(Equal("GitOpsResourcesGenerated"))
Expect(gitopsCondition.Status).To(Equal(metav1.ConditionTrue))

// set the annotation and update a spec to force enter the reconcile
setForceGenerateGitopsAnnotation(createdHasComp, "true")
createdHasComp.Spec.TargetPort = 1111
Expect(k8sClient.Update(ctx, createdHasComp)).Should(Succeed())

createdHasComp = &appstudiov1alpha1.Component{}
// Verify that the GitOpsResourcesForceGenerated status condition was set
Eventually(func() bool {
var gitOpsForceGenerateCheck bool

k8sClient.Get(context.Background(), hasCompLookupKey, createdHasComp)
for _, condition := range createdHasComp.Status.Conditions {
if condition.Type == "GitOpsResourcesForceGenerated" && condition.Status == metav1.ConditionTrue {
gitOpsForceGenerateCheck = true
}
}
return gitOpsForceGenerateCheck
}, timeout, interval).Should(BeTrue())
gitopsCondition = createdHasComp.Status.Conditions[len(createdHasComp.Status.Conditions)-1]
Expect(gitopsCondition.Type).To(Equal("GitOpsResourcesForceGenerated"))
Expect(gitopsCondition.Status).To(Equal(metav1.ConditionTrue))

hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace}

// Delete the specified HASComp resource
deleteHASCompCR(hasCompLookupKey)

// Delete the specified HASApp resource
deleteHASAppCR(hasAppLookupKey)
})
})

})

Expand Down

0 comments on commit c3e6afd

Please sign in to comment.