Operator SDK FAQ

    Operator SDK offers additional features on top of the basic project scaffolding that Kubebuilder provides. By default, operator-sdk init generates a project integrated with:

    • Operator Lifecycle Manager, an installation and runtime management system for operators
    • , a community hub for publishing operators
    • Operator SDK scorecard, a tool for ensuring operator best-practices and developing cluster tests

    Operator SDK supports operator types other than Go as well, such as Ansible and Helm.

    For further context about the relationship between Kubebuilder and Operator SDK, see .

    Can I use the Kubebuilder docs?

    Yes, you can use . Just keep in mind that when you see an instruction such as: $ kubebuilder <command> you will use $ operator-sdk <command>.

    Controller Runtime FAQ

    Please see the upstream first for any questions related to runtime mechanics or controller-runtime APIs.

    Can I customize the projects initialized with operator-sdk?

    After using the CLI to create your project, you are free to customize based on how you see fit. Please note that it is not recommended to deviate from the proposed layout unless you know what you are doing.

    For example, you should refrain from moving the scaffolded files, doing so will make it difficult to upgrade your project in the future. You may also lose the ability to use some of the CLI features and helpers. For further information on the project layout, see the doc

    You should not have separate logic. Instead design your reconciler to be idempotent. See the controller-runtime FAQ for more details.

    When my Custom Resource is deleted, I need to know its contents or perform cleanup tasks. How can I do that?

    Use a finalizer.

    I see the warning in my Operator’s logs: The resourceVersion for the provided watch is too old. What’s wrong?

    This is completely normal and expected behavior.

    This warning should not be stifled. It ensures that the informer is not stuck or wedged.

    Never seeing this warning may suggest that your watch or cache is not healthy. If the message is repeating every few seconds, this may signal a network connection problem or issue with etcd.

    For more information on kube-apiserver request timeout options, see the Kubernetes API Server Command Line Tool Reference

    My Ansible module is missing a dependency. How do I add it to the image?

    Unfortunately, adding the entire dependency tree for all Ansible modules would be excessive. Fortunately, you can add it easily. Simply edit your build/Dockerfile. You’ll want to change to root for the install command, just be sure to swap back using a series of commands like the following right after the FROM line.

    If you aren’t sure what dependencies are required, start up a container using the image in the FROM line as root. That will look something like this:

    1. docker run -u 0 -it --rm --entrypoint /bin/bash quay.io/operator-framework/ansible-operator:<sdk-tag-version>

    If you run into the following error message, it means that your operator is unable to watch the resource:

    1. E0320 15:42:17.676888 1 reflector.go:280] pkg/mod/k8s.io/client-go@v0.0.0-20191016111102-bec269661e48/tools/cache/reflector.go:96: Failed to watch *v1.ImageStreamTag: unknown (get imagestreamtags.image.openshift.io)
    2. {"level":"info","ts":1584718937.766342,"logger":"controller_memcached","msg":"ImageStreamTag resource not found.

    Using controller-runtime’s split client means that read operations (gets and lists) are read from a cache, and write operations are written directly to the API server. To populate the cache for reads, controller-runtime initiates a list and then a watch even when your operator is only attempting to get a single resource. The above scenario occurs when the operator does not have an RBAC permission to watch the resource. The solution is to add an RBAC directive to generate a config/rbac/role.yaml with watch privileges:

    Alternatively, if the resource you’re attempting to cannot be watched (like v1.ImageStreamTag above), you can specify that objects of this type should not be cached by adding the following to main.go:

    1. imagev1 "github.com/openshift/api/image/v1"
    2. )
    3. var (
    4. scheme = runtime.NewScheme()
    5. )
    6. func init() {
    7. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
    8. // Add imagev1's scheme.
    9. utilruntime.Must(imagev1.AddToScheme(scheme))
    10. }
    11. func main() {
    12. mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
    13. Scheme: scheme,
    14. // Specify that ImageStreamTag's should not be cached.
    15. ClientBuilder: manager.NewClientBuilder().WithUncached(&imagev1.ImageStreamTag{}),
    16. }

    Then in your controller file, add an RBAC directive to generate a config/rbac/role.yaml with get privileges:

    1. //+kubebuilder:rbac:groups=image.openshift.io,resources=imagestreamtags,verbs=get

    Now run make manifests to update your role.yaml.

    After deploying my operator, why do I see errors like “is forbidden: cannot set blockOwnerDeletion if an ownerReference refers to a resource you can’t set finalizers on: …”?

    For Helm and Ansible operators, this permission is configured by default. However for Go operators, it may be necessary to add this permission yourself by adding an RBAC directive to generate a config/rbac/role.yaml with update privileges on your CR’s finalizers:

    Now run make manifests to update your .

    When invoking make targets, why do I see errors like fork/exec /usr/local/kubebuilder/bin/etcd: no such file or directory occurred?

    If using an OS or distro that does not point sh to the bash shell (Ubuntu for example), add the following line to the Makefile:

    1. SHELL := /bin/bash

    How do I make my Operator proxy-friendly?


    Administrators can configure proxy-friendly Operators to support network proxies by specifiying HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables in the Operator deployment. (These variables can be handled by OLM.)

    Proxy-friendly Operators are responsible for inspecting the Operator environment and passing these variables along to the rquired operands. For more information and examples, please see the type-specific docs:

    The will be removed if set to false when running make bundle. Because of some underlying data structure changes and how yaml is unmarshalled, it is best to add them back in after they have been written.

    You can use this script to post process the files to add the preserveUnknownFields back in.

    1. function generate_preserveUnknownFieldsdata() {
    2. for j in config/crd/patches/*.yaml ; do
    3. if grep -qF "preserveUnknownFields" "$j";then
    4. variable=`awk '/metadata/{flag=1} flag && /name:/{print $NF;flag=""}' "$j"`
    5. for k in config/crd/bases/*.yaml ; do
    6. if grep -qF "$variable" "$j";then
    7. filename=`awk 'END{ var=FILENAME; split (var,a,/\//); print a[4]}' "$k"`
    8. awk '/^spec:/{print;print " preserveUnknownFields: false";next}1' "bundle/manifests/$filename" > testfile.tmp && mv testfile.tmp "bundle/manifests/$filename"
    9. fi
    10. done
    11. fi
    12. done
    13. }
    14. generate_preserveUnknownFieldsdata

    You can then modify the bundle target in your by adding a call to the script at the end of the target. See the example below:

    Note: Though this is a bug with controller-gen which is used by Operator SDK to generate CRD, this is a workaround from our end to enable users to preserve the field after controller-gen has run.