github.skymusic.top/operator-framework/operator-sdk@v0.8.2/doc/ansible/dev/testing_guide.md (about)

     1  # Testing Ansible Operators with Molecule
     2  
     3  ## Getting started
     4  
     5  ### Requirements
     6  To begin, you sould have:
     7  - The latest version of the [operator-sdk](https://github.com/operator-framework/operator-sdk) installed.
     8  - Docker installed and running
     9  - [Molecule](https://github.com/ansible/molecule) >= v2.20 (currently that will require installation from source, `pip install git+https://github.com/ansible/molecule.git`)
    10  - [Ansible](https://github.com/ansible/ansible) >= v2.7
    11  - [jmespath](https://pypi.org/project/jmespath/)
    12  - [The OpenShift Python client](https://github.com/openshift/openshift-restclient-python) >= v0.8
    13  - An initialized Ansible Operator project, with the molecule directory present. If you initialized a project with a previous
    14    version of operator-sdk, you can generate a new dummy project and copy in the `molecule` directory. Just be sure
    15    to generate the dummy project with the same `api-version` and `kind`, or some of the generated files will not work
    16    without modification. Your top-level project structure should look like this:
    17      ```
    18      .
    19      ├── build
    20      ├── deploy
    21      ├── molecule
    22      ├── roles
    23      ├── playbook.yml (optional)
    24      └── watches.yaml
    25      ```
    26  
    27  ### Molecule scenarios
    28  If you look into the `molecule` directory, you will see three directories (`default`, `test-local`, `test-cluster`).
    29  Each of those directories contains a set of files that together make up what is known as a molecule *scenario*.
    30  
    31  Our molecule scenarios have the following basic structure:
    32  
    33  ```
    34  .
    35  ├── molecule.yml
    36  ├── prepare.yml
    37  └── playbook.yml
    38  ```
    39  
    40  `molecule.yml` is a configuration file for molecule. It defines what driver to use to stand up an environment and the associated configuration, linting rules, and a variety of other configuration options. For full documentation on the options available here, see the [molecule configuration documentation](https://molecule.readthedocs.io/en/latest/configuration.html)
    41  
    42  `prepare.yml` is an Ansible playbook that is run once during the set up of a scenario. You
    43  can put any arbitrary Ansible in this playbook. It is used for one-time configuration
    44  of your test environment, for example, creating the cluster-wide `CustomResourceDefinition`
    45  that your Operator will watch.
    46  
    47  `playbook.yml` is an Ansible playbook that contains your core logic for the scenario. In a
    48  normal molecule scenario, this would import and run the associated role. For Ansible
    49  Operator, we mostly use this to create the Kubernetes resources and then execute a
    50  series of asserts that verify your cluster state.
    51  
    52  Below we will walk through the structure and function of each file for each scenario.
    53  
    54  #### default
    55  The default scenario is intended for use during the development of your Ansible role or playbook, and will run it
    56  outside of the context of an operator.
    57  You can run this scenario with
    58  `molecule test`
    59  or
    60  `molecule converge`. There is no corresponding `operator-sdk` command for this scenario.
    61  
    62  The scenario has the following structure:
    63  
    64  ```
    65  molecule/default
    66  ├── asserts.yml
    67  ├── molecule.yml
    68  ├── playbook.yml
    69  └── prepare.yml
    70  ```
    71  
    72  `asserts.yml` is an Ansible playbook contains Ansible assert tasks that will be run by all three scenarios.
    73  If you would like to write specific asserts for individual scenarios, you can instead remove the `asserts.yml`
    74  playbook import from that scenario's `playbook.yml`, or if you only want to add additional asserts, you can
    75  create a new playbook in that scenario and import it at the bottom of that scenario's `playbook.yml`.
    76  
    77  `molecule.yml` for this scenario tells molecule to use the docker driver to bring up a Kubernetes-in-Docker container,
    78  and exposes the API on the host's port 9443. It also specifies a few inventory and environment
    79  variables which are used in `prepare.yml` and `playbook.yml`.
    80  
    81  `prepare.yml` ensures that a kubeconfig properly configured to connect to the Kubernetes-in-Docker cluster exists and is mapped to the proper port, and also waits for the Kubernetes API to become
    82  available before allowing testing to begin.
    83  
    84  `playbook.yml` only imports your role or playbook and then imports the `asserts.yml` playbook.
    85  
    86  #### test-local
    87  The test-local scenario is a more full integration test of your operator. It brings up a Kubernetes-in-docker cluster, builds your Operator, deploys it
    88  into the cluster, and then creates an instance of your CustomResource and runs your assertions to make sure the Operator responded properly. You can run
    89  this scenario with
    90  `molecule test -s local`, which is equivalent to `operator-sdk test local`, or with `molecule converge -s test-local`, which will leave the environment up
    91  afterward.
    92  
    93  The scenario has the following structure:
    94  
    95  ```
    96  molecule/test-local
    97  ├── molecule.yml
    98  ├── playbook.yml
    99  └── prepare.yml
   100  ```
   101  
   102  `molecule.yml` for this scenario tells molecule to use the docker driver to bring up a Kubernetes-in-Docker container with the project root mounted,
   103  and exposes the API on the host's port 10443. It also specifies a few inventory and environment
   104  variables which are used in `prepare.yml` and `playbook.yml`. It is very similar to the default scenario's configuration.
   105  
   106  `prepare.yml` first runs the `prepare.yml` from the default scenario to ensure the kubeconfig is present and the API is up. It then creates the CustomResourceDefinition, namespace, and RBAC
   107  resources specified in the `deploy/` directory.
   108  
   109  `playbook.yml` is the most complicated file in this project. First, it connects to your
   110  Kubernetes-in-Docker container, and uses your mounted project root to build your Operator.
   111  This makes your Operator available to the cluster without needing to push it to an external
   112  registry. Next, it will ensure that a fresh deployment of your Operator is present in the
   113  cluster, and once there is it will create an instance of your Custom Resource
   114  (specified in `deploy/crds/`). It will then wait for the CustomResource to report a successful
   115  run, and once it has, will import the `asserts.yml` from the default scenario.
   116  
   117  #### test-cluster
   118  The test-cluster scenario is intended as a full integration test against
   119  an existing Kubernetes cluster, and assumes that the cluster is already available, the dependent resources from the `deploy/` directory
   120  are created, the operator image is built with `--enable-tests`, and that the image is available in a container registry. It connects
   121  to the existing Kubernetes cluster and deploys the test Operator, creates a Custom Resource, and runs your asserts.  You shouldn't
   122  call this scenario directly, rather you should build your operator with the `--enable-tests` flag, in which case a new entrypoint will
   123  be added that runs this scenario when the container starts up. It is recommended that you only interact with this scenario through
   124  `operator-sdk test cluster`.
   125  
   126  The scenario has the following structure:
   127  
   128  ```
   129  molecule/test-cluster
   130  ├── molecule.yml
   131  └── playbook.yml
   132  ```
   133  `molecule.yml` for this scenario is very simple, as it assumes an environment is already
   134  present. It essentially is just specifying the metadata of the scenario, and telling molecule
   135  not to try and create or destroy anything when run.
   136  
   137  `playbook.yml` is also pretty simple, compared to the previous scenarios. All it does is create
   138  an instance of your Custom Resource (specified in `deploy/crds`), and then import the `asserts.yml` from the `default` scenario.
   139  
   140  #### converge vs test
   141  The two most common molecule commands for testing during development are `molecule test` and `molecule converge`.
   142  `molecule test` performs a full loop, bringing a cluster up, preparing it, running your tasks, and tearing it down.
   143  `molecule converge` is more useful for iterative development, as it leaves your environment up between runs. This
   144  can cause unexpected problems if you end up corrupting your environment during testing, but running `molecule destroy`
   145  will reset it.
   146  
   147  
   148  
   149  ## operator-sdk test commands
   150  
   151  ### test local
   152  
   153  The `operator-sdk test local` command kicks off an end-to-end test of your Operator. It will bring up a [Kubernetes-in-Docker (kind)](https://github.com/bsycorp/kind) cluster, builds your Operator
   154  image and make it available to that cluster, create all the required resources from the `deploy/` directory, create an instance of your
   155  Custom Resource (specified in the `deploy/crds` directory), and then verify that the Operator has responded appropriately by running
   156  the asserts from `molecule/default/asserts.yml`.
   157  
   158  
   159  ### test cluster
   160  
   161  The `operator-sdk test cluster` command does much less than the `test local` command. It is intended as a full integration test against
   162  an existing Kubernetes cluster, and assumes that the cluster is already available, the dependent resources from the `deploy/` directory
   163  are created, the operator image is built with `--enable-tests`, and that the image is available in a container registry. When you run the command, it will connect
   164  to the existing Kubernetes cluster and deploy the test Operator, create a Custom Resource, and run the asserts in `molecule/default/asserts.yml`.
   165  
   166  ## Writing tests
   167  
   168  ### Adding a task
   169  The default operator that is generated by `operator-sdk new` doesn't do anything, so first we will need to add an
   170  Ansible task so that the Operator does something we can verify. For this example, we will create a simple ConfigMap
   171  with a single key.
   172  We'll be adding the task to `roles/example/tasks/main.yml`, which should now look like this:
   173  
   174  ```
   175  ---
   176  # tasks file for exampleapp
   177  - name: create Example configmap
   178    k8s:
   179      definition:
   180        apiVersion: v1
   181        kind: ConfigMap
   182        metadata:
   183          name: 'test-data'
   184          namespace: '{{ meta.namespace }}'
   185        data:
   186          hello: world
   187  ```
   188  
   189  
   190  
   191  ### Adding a test
   192  
   193  Now that our Operator actually does some work, we can add a corresponding assert to `molecule/default/asserts.yml`.
   194  We'll also add a debug message so that we can see what the ConfigMap looks like.
   195  The file should now look like this:
   196  
   197  ```
   198  ---
   199  
   200  - name: Verify
   201    hosts: localhost
   202    connection: local
   203    vars:
   204      ansible_python_interpreter: '{{ ansible_playbook_python }}'
   205    tasks:
   206      - debug: var=cm
   207        vars:
   208          cm: '{{ lookup("k8s", api_version="v1", kind="ConfigMap", namespace=namespace, resource_name="test-data") }}'
   209      - assert:
   210          that: cm.data.hello == 'world'
   211        vars:
   212          cm: '{{ lookup("k8s", api_version="v1", kind="ConfigMap", namespace=namespace, resource_name="test-data") }}'
   213  ```
   214  
   215  Now that we have a functional Operator, and an assertion of its behavior, we can verify that everything is working
   216  by running `operator-sdk test local`.
   217  
   218  #### The Ansible `assert` and `fail` modules
   219  These modules are handy for adding assertions and failure conditions to your Ansible Operator tests:
   220  
   221  - [assert](https://docs.ansible.com/ansible/latest/modules/assert_module.html)
   222  - [fail](https://docs.ansible.com/ansible/latest/modules/fail_module.html)