-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Conditions to status #91
Changes from 1 commit
39b3c2e
29631dc
f3d0987
1a3d231
407cd2f
4e05d47
9f797cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,10 @@ See the License for the specific language governing permissions and | |
limitations under the License. | ||
*/ | ||
|
||
package controllers | ||
package controllers_test | ||
|
||
import ( | ||
"context" | ||
"path/filepath" | ||
"testing" | ||
|
||
|
@@ -31,6 +32,10 @@ import ( | |
"sigs.k8s.io/controller-runtime/pkg/log/zap" | ||
|
||
operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" | ||
"github.com/operator-framework/operator-controller/controllers" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
//+kubebuilder:scaffold:imports | ||
) | ||
|
||
|
@@ -78,3 +83,60 @@ var _ = AfterSuite(func() { | |
err := testEnv.Stop() | ||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
|
||
var _ = Describe("Reconcile Test", func() { | ||
When("an Operator is created", func() { | ||
var ( | ||
operator *operatorsv1alpha1.Operator | ||
ctx context.Context | ||
opName string | ||
pkgName string | ||
err error | ||
) | ||
BeforeEach(func() { | ||
ctx = context.Background() | ||
|
||
opName = "operator-test" | ||
pkgName = "package-test" | ||
tmshort marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
operator = &operatorsv1alpha1.Operator{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: opName, | ||
}, | ||
Spec: operatorsv1alpha1.OperatorSpec{ | ||
PackageName: pkgName, | ||
}, | ||
} | ||
err = k8sClient.Create(ctx, operator) | ||
Expect(err).To(Not(HaveOccurred())) | ||
|
||
or := controllers.OperatorReconciler{ | ||
k8sClient, | ||
scheme.Scheme, | ||
} | ||
_, err = or.Reconcile(ctx, reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: opName, | ||
}, | ||
}) | ||
Expect(err).To(Not(HaveOccurred())) | ||
}) | ||
AfterEach(func() { | ||
err = k8sClient.Delete(ctx, operator) | ||
Expect(err).To(Not(HaveOccurred())) | ||
}) | ||
It("has a Condition created", func() { | ||
getOperator := &operatorsv1alpha1.Operator{} | ||
|
||
err = k8sClient.Get(ctx, client.ObjectKey{ | ||
Name: opName, | ||
}, getOperator) | ||
Expect(err).To(Not(HaveOccurred())) | ||
|
||
// There should always be a "Ready" condition, regardless of Status. | ||
conds := getOperator.Status.Conditions | ||
Expect(conds).To(Not(BeEmpty())) | ||
Expect(conds).To(ContainElement(HaveField("Type", operatorsv1alpha1.TypeReady))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also test for the specific Status and Reason we expect? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to not bother with that. As I wanted to minimize changes (vs. additions) to the tests as code is added. |
||
}) | ||
Comment on lines
+130
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, but perhaps some good future proofing to do while things aren't already baked. Could we declare a slice of all of our condition types in the same place the we define them, and then in this test iterate that slice to make sure that all of them exist and have the generation of the |
||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't seen (and I haven't looked very hard) patchStrategy and patchMergeKey on conditions before. Is this something suggested in official k8s docs somewhere?
I'm wondering what the implications are? It seems like this would permit two writers to server-side apply two different types concurrently? Do we want that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, these are the recommended attributes:
https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Condition
What it should mean is that you can only have a single instance of each type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also need the other tags for
Conditions
that are listed in the exampleFooStatus
on that page? I'm not actually sure, but it's definitely worth adding them and runningmake generate manifests
to see what they do to the generated code and/or CRD file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does add something (
x-kubernetes-list
) values. So I might as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another follow-up/TODO/ground rule/invariant:
For every
condition.Type
that we define, that type has a condition set no matter what code path is taken through the reconciler. Every single time reconcile is called, we make sure to evaluate what the value of eachcondition.Type
should be and we make sure it is set that way.TL;DR:
Where input is defined as: "Operator CR (minus its status) + existing cluster state"
And output is defined as: "Operator CR status + new desired cluster state"