Skip to content

Commit

Permalink
Merge pull request #1482 from raulcabello/bundle-error-state
Browse files Browse the repository at this point in the history
Add ignore conditions
  • Loading branch information
raulcabello committed Apr 20, 2023
2 parents bdfbfe4 + 585f357 commit ab055fa
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 3 deletions.
48 changes: 48 additions & 0 deletions charts/fleet-crd/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,18 @@ spec:
waitForJobs:
type: boolean
type: object
ignore:
properties:
conditions:
items:
additionalProperties:
nullable: true
type: string
nullable: true
type: object
nullable: true
type: array
type: object
keepResources:
type: boolean
kustomize:
Expand Down Expand Up @@ -558,6 +570,18 @@ spec:
waitForJobs:
type: boolean
type: object
ignore:
properties:
conditions:
items:
additionalProperties:
nullable: true
type: string
nullable: true
type: object
nullable: true
type: array
type: object
keepResources:
type: boolean
kustomize:
Expand Down Expand Up @@ -1075,6 +1099,18 @@ spec:
waitForJobs:
type: boolean
type: object
ignore:
properties:
conditions:
items:
additionalProperties:
nullable: true
type: string
nullable: true
type: object
nullable: true
type: array
type: object
keepResources:
type: boolean
kustomize:
Expand Down Expand Up @@ -1226,6 +1262,18 @@ spec:
waitForJobs:
type: boolean
type: object
ignore:
properties:
conditions:
items:
additionalProperties:
nullable: true
type: string
nullable: true
type: object
nullable: true
type: array
type: object
keepResources:
type: boolean
kustomize:
Expand Down
62 changes: 59 additions & 3 deletions modules/agent/pkg/deployer/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package deployer

import (
"encoding/json"
"fmt"
"sort"

jsonpatch "github.com/evanphx/json-patch"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/rancher/wrangler/pkg/merr"
"github.com/rancher/wrangler/pkg/objectset"
"github.com/rancher/wrangler/pkg/summary"
"github.com/sirupsen/logrus"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -166,7 +168,7 @@ func (m *Manager) MonitorBundle(bd *fleet.BundleDeployment) (DeploymentStatus, e
return status, err
}

status.NonReadyStatus = nonReady(plan)
status.NonReadyStatus = nonReady(plan, bd.Spec.Options.IgnoreOptions)
status.ModifiedStatus = modified(plan, resourcesPreviuosRelease)
status.Ready = false
status.NonModified = false
Expand Down Expand Up @@ -262,7 +264,7 @@ func isResourceInPreviousRelease(key objectset.ObjectKey, kind string, objsPrevi
return false
}

func nonReady(plan apply.Plan) (result []fleet.NonReadyStatus) {
func nonReady(plan apply.Plan, ignoreOptions fleet.IgnoreOptions) (result []fleet.NonReadyStatus) {
defer func() {
sort.Slice(result, func(i, j int) bool {
return result[i].UID < result[j].UID
Expand All @@ -274,6 +276,12 @@ func nonReady(plan apply.Plan) (result []fleet.NonReadyStatus) {
return
}
if u, ok := obj.(*unstructured.Unstructured); ok {
if ignoreOptions.Conditions != nil {
if err := excludeIgnoredConditions(u, ignoreOptions); err != nil {
logrus.Errorf("failed to ignore conditions: %v", err)
}
}

summary := summary.Summarize(u)
if !summary.IsReady() {
result = append(result, fleet.NonReadyStatus{
Expand All @@ -288,5 +296,53 @@ func nonReady(plan apply.Plan) (result []fleet.NonReadyStatus) {
}
}

return
return result
}

// excludeIgnoredConditions removes the conditions that are included in ignoreOptions from the object passed as a parameter
func excludeIgnoredConditions(obj *unstructured.Unstructured, ignoreOptions fleet.IgnoreOptions) error {
conditions, _, err := unstructured.NestedSlice(obj.Object, "status", "conditions")
if err != nil {
return err
}
conditionsWithoutIgnored := make([]interface{}, 0)

for _, condition := range conditions {
condition, ok := condition.(map[string]interface{})
if !ok {
return fmt.Errorf("condition: %#v can't be converted to map[string]interface{}", condition)
}
excludeCondition := false
for _, ignoredCondition := range ignoreOptions.Conditions {
if shouldExcludeCondition(condition, ignoredCondition) {
excludeCondition = true
break
}
}
if !excludeCondition {
conditionsWithoutIgnored = append(conditionsWithoutIgnored, condition)
}
}

err = unstructured.SetNestedSlice(obj.Object, conditionsWithoutIgnored, "status", "conditions")
if err != nil {
return err
}

return nil
}

// shouldExcludeCondition returns true if all the elements of ignoredConditions are inside conditions
func shouldExcludeCondition(conditions map[string]interface{}, ignoredConditions map[string]string) bool {
if len(ignoredConditions) > len(conditions) {
return false
}

for k, v := range ignoredConditions {
if vc, found := conditions[k]; !found || vc != v {
return false
}
}

return true
}
78 changes: 78 additions & 0 deletions modules/agent/pkg/deployer/monitor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package deployer

import (
"testing"

"github.com/google/go-cmp/cmp"
fleet "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

func TestExcludeIgnoredConditions(t *testing.T) {
podInitializedAndNotReady := v1.Pod{Status: v1.PodStatus{
Conditions: []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionFalse}, {Type: v1.PodInitialized, Status: v1.ConditionTrue}},
}}
uPodInitializedAndNotReady, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&podInitializedAndNotReady)
if err != nil {
t.Errorf("can't convert podInitializedAndNotReady to unstructured: %v", err)
}
podInitialized := v1.Pod{Status: v1.PodStatus{
Conditions: []v1.PodCondition{{Type: v1.PodInitialized, Status: v1.ConditionTrue}},
}}
uPodInitialized, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&podInitialized)
if err != nil {
t.Errorf("can't convert podInitialized to unstructured: %v", err)
}
tests := map[string]struct {
obj *unstructured.Unstructured
ignoreOptions fleet.IgnoreOptions
expectedObj *unstructured.Unstructured
expectedErr error
}{
"nothing is changed without IgnoreOptions": {
obj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
ignoreOptions: fleet.IgnoreOptions{},
expectedObj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
expectedErr: nil,
},
"nothing is changed when IgnoreOptions don't match any condition": {
obj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
ignoreOptions: fleet.IgnoreOptions{Conditions: []map[string]string{{"Not": "Found"}}},
expectedObj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
expectedErr: nil,
},
"'Type: Ready' condition is excluded when IgnoreOptions contains 'Type: Ready' condition": {
obj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
ignoreOptions: fleet.IgnoreOptions{Conditions: []map[string]string{{"type": "Ready"}}},
expectedObj: &unstructured.Unstructured{Object: uPodInitialized},
expectedErr: nil,
},
"'Type: Ready' condition is excluded when IgnoreOptions contains 'Type: Ready, status: False' condition": {
obj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
ignoreOptions: fleet.IgnoreOptions{Conditions: []map[string]string{{"type": "Ready", "status": "False"}}},
expectedObj: &unstructured.Unstructured{Object: uPodInitialized},
expectedErr: nil,
},
"nothing is changed when IgnoreOptions contains 'type: Ready, status: True' condition": {
obj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
ignoreOptions: fleet.IgnoreOptions{Conditions: []map[string]string{{"type": "Ready", "status": "True"}}},
expectedObj: &unstructured.Unstructured{Object: uPodInitializedAndNotReady},
expectedErr: nil,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
obj := test.obj
err := excludeIgnoredConditions(obj, test.ignoreOptions)
if err != test.expectedErr {
t.Errorf("expected error doesn't match: expected %v, got %v", test.expectedErr, err)
}
if !cmp.Equal(obj, test.expectedObj) {
t.Errorf("objects don't match: expected %v, got %v", test.expectedObj, obj)
}
})
}
}
8 changes: 8 additions & 0 deletions pkg/apis/fleet.cattle.io/v1alpha1/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ type BundleDeploymentOptions struct {

// KeepResources can be used to keep the deployed resources when removing the bundle
KeepResources bool `json:"keepResources,omitempty"`

//IgnoreOptions can be used to ignore fields when monitoring the bundle.
IgnoreOptions `json:"ignore,omitempty"`
}

type DiffOptions struct {
Expand Down Expand Up @@ -312,6 +315,11 @@ type HelmOptions struct {
DisablePreProcess bool `json:"disablePreProcess,omitempty"`
}

// IgnoreOptions defines conditions to be ignored when monitoring the Bundle.
type IgnoreOptions struct {
Conditions []map[string]string `json:"conditions,omitempty"`
}

// Define helm values that can come from configmap, secret or external. Credit: https://github.com/fluxcd/helm-operator/blob/0cfea875b5d44bea995abe7324819432070dfbdc/pkg/apis/helm.fluxcd.io/v1/types_helmrelease.go#L439
type ValuesFrom struct {
// The reference to a config map with release values.
Expand Down
30 changes: 30 additions & 0 deletions pkg/apis/fleet.cattle.io/v1alpha1/zz_generated_deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ab055fa

Please sign in to comment.