Update old stable-5 branch to sync with current changes from main (#264)

* Update tests for newer version of openshift (#254)

* Update tests for newer version of openshift

More recent versions of ocp no longer automatically create tokens for
service accounts. This updates the tests to manually create the tokens.

* Update nginx template version

The old image was EOL and the deployment was failing to deploy.

* Fix nginx version for all tasks

* Add missing var

* Remove openshift inventory plugin (#252)

* Remove openshift inventory plugin

This removes the openshift inventory plugin which has been deprecated
since version 3.0.0. The tests have been updated to retain coverage of
the connection plugin, which is still supported.

* Update version in Makefile

* CI fixes

* Update version info in build scripts

* Set ansible remote directory

The security policy on the pod is preventing ansible from writing to /.
Set it to /tmp which should be writable.

* Bump the ansible-lint version to 25.1.2 (#255)

* Bump the ansible-lint version to 25.1.2

* Update changelogs/fragments/ansible-lint-update.yml

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>

---------

Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>

* Add ansible-lint to tox linters (#258)

* Add ansible-lint to tox linters

* Bump black

* Black formatting

* fix linting

* prepare release 4.0.2 (#262) (#263)

(cherry picked from commit 55ccaf3394)

* Update k8s dependency upper bounds (#257)

---------

Co-authored-by: Mike Graves <mgraves@redhat.com>
Co-authored-by: GomathiselviS <gomathiselvi@gmail.com>
Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
Co-authored-by: Bianca Henderson <beeankha@gmail.com>
This commit is contained in:
Mandar Kulkarni
2025-06-05 12:09:02 -07:00
committed by GitHub
parent d1788bed2d
commit 620de63a26
23 changed files with 101 additions and 319 deletions

View File

@@ -26,4 +26,4 @@ jobs:
- uses: ansible-network/github_actions/.github/actions/checkout_dependency@main - uses: ansible-network/github_actions/.github/actions/checkout_dependency@main
- name: run-ansible-lint - name: run-ansible-lint
uses: ansible/ansible-lint@v24.7.0 uses: ansible/ansible-lint@v25.1.2

View File

@@ -4,6 +4,14 @@ OKD Collection Release Notes
.. contents:: Topics .. contents:: Topics
v4.0.2
======
Release Summary
---------------
This patch updates the k8s dependency version to the 5.x range and modifies tests to handle the manual creation of service account tokens.
v4.0.1 v4.0.1
====== ======

View File

@@ -1,7 +1,7 @@
.PHONY: molecule .PHONY: molecule
# Also needs to be updated in galaxy.yml # Also needs to be updated in galaxy.yml
VERSION = 4.0.1 VERSION = 6.0.0-dev0
SANITY_TEST_ARGS ?= --docker --color SANITY_TEST_ARGS ?= --docker --color
UNITS_TEST_ARGS ?= --docker --color UNITS_TEST_ARGS ?= --docker --color

View File

@@ -92,7 +92,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible
--- ---
collections: collections:
- name: community.okd - name: community.okd
version: 4.0.1 version: 4.0.2
``` ```
### Installing the Kubernetes Python Library ### Installing the Kubernetes Python Library

View File

@@ -250,3 +250,10 @@ releases:
fragments: fragments:
- 242-fix-failed-token-deletion.yml - 242-fix-failed-token-deletion.yml
release_date: '2024-12-03' release_date: '2024-12-03'
4.0.2:
changes:
release_summary: This patch updates the k8s dependency version to the 5.x range
and modifies tests to handle the manual creation of service account tokens.
fragments:
- 4.0.2.yml
release_date: '2025-05-28'

View File

@@ -1,2 +0,0 @@
bugfixes:
- openshift_auth - fix issue where openshift_auth module sometimes does not delete the auth token. Based on stale PR (https://github.com/openshift/community.okd/pull/194).

View File

@@ -0,0 +1,3 @@
---
minor_changes:
- Bump version of ansible-lint to 25.1.2 (https://github.com/openshift/community.okd/pull/255).

View File

@@ -9,7 +9,7 @@
# - All functions are prefixed with f_ so it's obvious where they come # - All functions are prefixed with f_ so it's obvious where they come
# from when in use throughout the script # from when in use throughout the script
DOWNSTREAM_VERSION="4.0.1" DOWNSTREAM_VERSION="6.0.0-dev0"
KEEP_DOWNSTREAM_TMPDIR="${KEEP_DOWNSTREAM_TMPDIR:-''}" KEEP_DOWNSTREAM_TMPDIR="${KEEP_DOWNSTREAM_TMPDIR:-''}"
INSTALL_DOWNSTREAM_COLLECTION_PATH="${INSTALL_DOWNSTREAM_COLLECTION_PATH:-}" INSTALL_DOWNSTREAM_COLLECTION_PATH="${INSTALL_DOWNSTREAM_COLLECTION_PATH:-}"
_build_dir="" _build_dir=""

View File

@@ -5,7 +5,7 @@ authors:
- willthames (https://github.com/willthames) - willthames (https://github.com/willthames)
- Akasurde (https://github.com/akasurde) - Akasurde (https://github.com/akasurde)
dependencies: dependencies:
kubernetes.core: '>=5.0.0' kubernetes.core: '>=6.0.0'
description: OKD Collection for Ansible. description: OKD Collection for Ansible.
documentation: '' documentation: ''
homepage: '' homepage: ''
@@ -24,4 +24,4 @@ tags:
- okd - okd
- cluster - cluster
# Also needs to be updated in the Makefile # Also needs to be updated in the Makefile
version: 4.0.1 version: 6.0.0-dev0

View File

@@ -19,11 +19,10 @@ plugin_routing:
redirect: community.okd.openshift_auth redirect: community.okd.openshift_auth
inventory: inventory:
openshift: openshift:
deprecation: tombstone:
removal_version: 5.0.0 removal_version: 5.0.0
warning_text: >- warning_text: >-
The openshift inventory plugin has been deprecated and Use kubernetes.core.k8s_info and ansible.builtin.add_host instead.
will be removed in release 5.0.0.
action: action:
k8s: k8s:
redirect: kubernetes.core.k8s_info redirect: kubernetes.core.k8s_info

View File

@@ -1,6 +1,7 @@
# Want to make sure comments don't break it # Want to make sure comments don't break it
export NAME=test123 export NAME=test123
NAMESPACE=openshift NAMESPACE=openshift
NGINX_VERSION=1.22-ubi8

View File

@@ -14,12 +14,7 @@ provisioner:
log: true log: true
options: options:
vvv: True vvv: True
config_options:
inventory:
enable_plugins: community.okd.openshift
inventory: inventory:
hosts:
plugin: community.okd.openshift
host_vars: host_vars:
localhost: localhost:
virtualenv: ${MOLECULE_EPHEMERAL_DIRECTORY}/virtualenv virtualenv: ${MOLECULE_EPHEMERAL_DIRECTORY}/virtualenv

View File

@@ -3,6 +3,7 @@
- set_fact: - set_fact:
test_sa: "clusterrole-sa" test_sa: "clusterrole-sa"
test_ns: "clusterrole-ns" test_ns: "clusterrole-ns"
test_tn: "clusterrole-tn"
- name: Ensure namespace - name: Ensure namespace
kubernetes.core.k8s: kubernetes.core.k8s:
@@ -26,34 +27,27 @@
name: "{{ test_sa }}" name: "{{ test_sa }}"
namespace: "{{ test_ns }}" namespace: "{{ test_ns }}"
- name: Read Service Account - name: Create SA token
kubernetes.core.k8s_info: kubernetes.core.k8s:
kind: ServiceAccount definition:
namespace: "{{ test_ns }}" apiVersion: v1
name: "{{ test_sa }}" kind: Secret
register: result metadata:
name: "{{ test_tn }}"
- set_fact: namespace: "{{ test_ns }}"
secret_token: "{{ result.resources[0]['secrets'][0]['name'] }}" annotations:
kubernetes.io/service-account.name: "{{ test_sa }}"
type: kubernetes.io/service-account-token
- name: Get secret details - name: Get secret details
kubernetes.core.k8s_info: kubernetes.core.k8s_info:
kind: Secret kind: Secret
namespace: '{{ test_ns }}' namespace: "{{ test_ns }}"
name: '{{ secret_token }}' name: "{{ test_tn }}"
register: _secret register: _secret
retries: 10
delay: 10
until:
- ("'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']") or ("'token' in _secret.resources[0]['data']")
- set_fact:
api_token: "{{ _secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}"
when: "'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']"
- set_fact: - set_fact:
api_token: "{{ _secret.resources[0]['data']['token'] | b64decode }}" api_token: "{{ _secret.resources[0]['data']['token'] | b64decode }}"
when: "'token' in _secret.resources[0]['data']"
- name: list Node should failed (forbidden user) - name: list Node should failed (forbidden user)
kubernetes.core.k8s_info: kubernetes.core.k8s_info:

View File

@@ -4,6 +4,7 @@
test_ns: "prune-roles" test_ns: "prune-roles"
sa_name: "roles-sa" sa_name: "roles-sa"
pod_name: "pod-prune" pod_name: "pod-prune"
tn_name: "roles-sa-token"
role_definition: role_definition:
- name: pod-list - name: pod-list
labels: labels:
@@ -50,34 +51,27 @@
name: '{{ sa_name }}' name: '{{ sa_name }}'
namespace: '{{ test_ns }}' namespace: '{{ test_ns }}'
- name: Read Service Account - name: Create SA secret
kubernetes.core.k8s_info: kubernetes.core.k8s:
kind: ServiceAccount definition:
namespace: '{{ test_ns }}' apiVersion: v1
name: '{{ sa_name }}' kind: Secret
register: sa_out metadata:
name: "{{ tn_name }}"
- set_fact: namespace: "{{ test_ns }}"
secret_token: "{{ sa_out.resources[0]['secrets'][0]['name'] }}" annotations:
kubernetes.io/service-account.name: "{{ sa_name }}"
type: kubernetes.io/service-account-token
- name: Get secret details - name: Get secret details
kubernetes.core.k8s_info: kubernetes.core.k8s_info:
kind: Secret kind: Secret
namespace: '{{ test_ns }}' namespace: '{{ test_ns }}'
name: '{{ secret_token }}' name: '{{ tn_name }}'
register: r_secret register: r_secret
retries: 10
delay: 10
until:
- ("'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']") or ("'token' in r_secret.resources[0]['data']")
- set_fact:
api_token: "{{ r_secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}"
when: "'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']"
- set_fact: - set_fact:
api_token: "{{ r_secret.resources[0]['data']['token'] | b64decode }}" api_token: "{{ r_secret.resources[0]['data']['token'] | b64decode }}"
when: "'token' in r_secret.resources[0]['data']"
- name: list resources using service account - name: list resources using service account
kubernetes.core.k8s_info: kubernetes.core.k8s_info:

View File

@@ -7,6 +7,7 @@
parameters: parameters:
NAMESPACE: openshift NAMESPACE: openshift
NAME: test123 NAME: test123
NGINX_VERSION: "{{ nginx_version }}"
register: result register: result
- name: Create the rendered resources - name: Create the rendered resources
@@ -32,6 +33,7 @@
parameters: parameters:
NAMESPACE: openshift NAMESPACE: openshift
NAME: test123 NAME: test123
NGINX_VERSION: "{{ nginx_version }}"
state: present state: present
namespace_target: process-test namespace_target: process-test
register: result register: result
@@ -44,6 +46,7 @@
NAMESPACE: openshift NAMESPACE: openshift
NAME: test123 NAME: test123
MEMORY_LIMIT: 1Gi MEMORY_LIMIT: 1Gi
NGINX_VERSION: "{{ nginx_version }}"
state: present state: present
namespace_target: process-test namespace_target: process-test
register: result register: result
@@ -55,6 +58,7 @@
parameters: parameters:
NAMESPACE: openshift NAMESPACE: openshift
NAME: test123 NAME: test123
NGINX_VERSION: "{{ nginx_version }}"
state: absent state: absent
namespace_target: process-test namespace_target: process-test
register: result register: result

View File

@@ -1,9 +1,31 @@
--- ---
- name: Verify inventory and connection plugins - name: Create inventory of pods
# This group is created by the openshift_inventory plugin # We need to manually create the inventory of pods now that the inventory plugin has been removed
# It is automatically configured to use the `oc` connection plugin gather_facts: false
hosts: localhost
connection: local
tasks:
- name: Get pods
kubernetes.core.k8s_info:
kind: Pod
namespace: testing
register: pods
- name: Add pods to inventory
ansible.builtin.add_host:
name: "{{ item.metadata.name }}"
groups:
- namespace_testing_pods
ansible_oc_pod: "{{ item.metadata.name }}"
ansible_oc_namespace: "{{ item.metadata.namespace }}"
pod_phase: "{{ item.status.phase }}"
ansible_remote_tmp: /tmp/.ansible
loop: "{{ pods.resources }}"
- name: Verify connection plugin
hosts: namespace_testing_pods hosts: namespace_testing_pods
gather_facts: no gather_facts: no
connection: community.okd.oc
vars: vars:
file_content: | file_content: |
Hello world Hello world
@@ -77,6 +99,7 @@
- import_tasks: tasks/openshift_process.yml - import_tasks: tasks/openshift_process.yml
vars: vars:
files_dir: '{{ playbook_dir }}/files' files_dir: '{{ playbook_dir }}/files'
nginx_version: 1.22-ubi8
always: always:
- name: Delete namespace - name: Delete namespace
community.okd.k8s: community.okd.k8s:

View File

@@ -1,252 +0,0 @@
# Copyright (c) 2018 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
name: openshift
author:
- Chris Houseknecht (@chouseknecht)
short_description: OpenShift inventory source
description:
- Fetch containers, services and routes for one or more clusters
- Groups by cluster name, namespace, namespace_services, namespace_pods, namespace_routes, and labels
- Uses openshift.(yml|yaml) YAML configuration file to set parameter values.
deprecated:
removed_in: 5.0.0
why: |
As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to
remove the openshift inventory plugin in release 5.0.0.
alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead."
options:
plugin:
description: token that ensures this is a source file for the 'openshift' plugin.
required: True
choices: ['openshift', 'community.okd.openshift']
connections:
description:
- Optional list of cluster connection settings. If no connections are provided, the default
I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
the active user is authorized to access.
suboptions:
name:
description:
- Optional name to assign to the cluster. If not provided, a name is constructed from the server
and port.
kubeconfig:
description:
- Path to an existing Kubernetes config file. If not provided, and no other connection
options are provided, the Kubernetes client will attempt to load the default
configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG
environment variable.
context:
description:
- The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
variable.
host:
description:
- Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
api_key:
description:
- Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
variable.
username:
description:
- Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
environment variable.
password:
description:
- Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
environment variable.
client_cert:
description:
- Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
environment variable.
aliases: [ cert_file ]
client_key:
description:
- Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE
environment variable.
aliases: [ key_file ]
ca_cert:
description:
- Path to a CA certificate used to authenticate with the API. Can also be specified via
K8S_AUTH_SSL_CA_CERT environment variable.
aliases: [ ssl_ca_cert ]
validate_certs:
description:
- "Whether or not to verify the API server's SSL certificates. Can also be specified via
K8S_AUTH_VERIFY_SSL environment variable."
type: bool
aliases: [ verify_ssl ]
namespaces:
description:
- List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized
to access.
requirements:
- "python >= 3.6"
- "kubernetes >= 12.0.0"
- "PyYAML >= 3.11"
"""
EXAMPLES = """
# File must be named openshift.yaml or openshift.yml
- name: Authenticate with token, and return all pods and services for all namespaces
plugin: community.okd.openshift
connections:
- host: https://192.168.64.4:8443
api_key: xxxxxxxxxxxxxxxx
verify_ssl: false
- name: Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
plugin: community.okd.openshift
connections:
- namespaces:
- testing
- name: Use a custom config file, and a specific context.
plugin: community.okd.openshift
connections:
- kubeconfig: /path/to/config
context: 'awx/192-168-64-4:8443/developer'
"""
try:
from ansible_collections.kubernetes.core.plugins.inventory.k8s import (
K8sInventoryException,
InventoryModule as K8sInventoryModule,
format_dynamic_api_exc,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
HAS_KUBERNETES_COLLECTION = True
except ImportError as e:
HAS_KUBERNETES_COLLECTION = False
try:
from kubernetes.dynamic.exceptions import DynamicApiError
except ImportError:
pass
class InventoryModule(K8sInventoryModule):
NAME = "community.okd.openshift"
connection_plugin = "community.okd.oc"
transport = "oc"
def check_kubernetes_collection(self):
if not HAS_KUBERNETES_COLLECTION:
raise K8sInventoryException(
"The kubernetes.core collection must be installed"
)
def fetch_objects(self, connections):
self.check_kubernetes_collection()
super(InventoryModule, self).fetch_objects(connections)
self.display.deprecated(
"The 'openshift' inventory plugin has been deprecated and will be removed in release 5.0.0",
version="5.0.0",
collection_name="community.okd",
)
if connections:
if not isinstance(connections, list):
raise K8sInventoryException("Expecting connections to be a list.")
for connection in connections:
client = get_api_client(**connection)
name = connection.get(
"name", self.get_default_host_name(client.configuration.host)
)
if connection.get("namespaces"):
namespaces = connection["namespaces"]
else:
namespaces = self.get_available_namespaces(client)
for namespace in namespaces:
self.get_routes_for_namespace(client, name, namespace)
else:
client = get_api_client()
name = self.get_default_host_name(client.configuration.host)
namespaces = self.get_available_namespaces(client)
for namespace in namespaces:
self.get_routes_for_namespace(client, name, namespace)
def get_routes_for_namespace(self, client, name, namespace):
self.check_kubernetes_collection()
v1_route = client.resources.get(
api_version="route.openshift.io/v1", kind="Route"
)
try:
obj = v1_route.get(namespace=namespace)
except DynamicApiError as exc:
self.display.debug(exc)
raise K8sInventoryException(
"Error fetching Routes list: %s" % format_dynamic_api_exc(exc)
)
namespace_group = "namespace_{0}".format(namespace)
namespace_routes_group = "{0}_routes".format(namespace_group)
self.inventory.add_group(name)
self.inventory.add_group(namespace_group)
self.inventory.add_child(name, namespace_group)
self.inventory.add_group(namespace_routes_group)
self.inventory.add_child(namespace_group, namespace_routes_group)
for route in obj.items:
route_name = route.metadata.name
route_annotations = (
{}
if not route.metadata.annotations
else dict(route.metadata.annotations)
)
self.inventory.add_host(route_name)
if route.metadata.labels:
# create a group for each label_value
for key, value in route.metadata.labels:
group_name = "label_{0}_{1}".format(key, value)
self.inventory.add_group(group_name)
self.inventory.add_child(group_name, route_name)
route_labels = dict(route.metadata.labels)
else:
route_labels = {}
self.inventory.add_child(namespace_routes_group, route_name)
# add hostvars
self.inventory.set_variable(route_name, "labels", route_labels)
self.inventory.set_variable(route_name, "annotations", route_annotations)
self.inventory.set_variable(
route_name, "cluster_name", route.metadata.clusterName
)
self.inventory.set_variable(route_name, "object_type", "route")
self.inventory.set_variable(
route_name, "self_link", route.metadata.selfLink
)
self.inventory.set_variable(
route_name, "resource_version", route.metadata.resourceVersion
)
self.inventory.set_variable(route_name, "uid", route.metadata.uid)
if route.spec.host:
self.inventory.set_variable(route_name, "host", route.spec.host)
if route.spec.path:
self.inventory.set_variable(route_name, "path", route.spec.path)
if hasattr(route.spec.port, "targetPort") and route.spec.port.targetPort:
self.inventory.set_variable(route_name, "port", dict(route.spec.port))

View File

@@ -295,9 +295,9 @@ class OpenShiftMigrateTemplateInstances(AnsibleOpenshiftModule):
object_type in transforms.keys() object_type in transforms.keys()
and obj["ref"].get("apiVersion") != transforms[object_type] and obj["ref"].get("apiVersion") != transforms[object_type]
): ):
ti_elem["status"]["objects"][i]["ref"][ ti_elem["status"]["objects"][i]["ref"]["apiVersion"] = (
"apiVersion" transforms[object_type]
] = transforms[object_type] )
ti_to_be_migrated.append(ti_elem) ti_to_be_migrated.append(ti_elem)
return ti_to_be_migrated return ti_to_be_migrated

View File

@@ -225,7 +225,7 @@ def get_oauthaccesstoken_objectname_from_token(token_name):
sha256Prefix = "sha256~" sha256Prefix = "sha256~"
if token_name.startswith(sha256Prefix): if token_name.startswith(sha256Prefix):
content = token_name[len(sha256Prefix):] content = token_name[len(sha256Prefix) :]
else: else:
content = token_name content = token_name
b64encoded = urlsafe_b64encode(hashlib.sha256(content.encode()).digest()).rstrip( b64encoded = urlsafe_b64encode(hashlib.sha256(content.encode()).digest()).rstrip(

View File

@@ -421,9 +421,9 @@ class OpenShiftRoute(AnsibleOpenshiftModule):
if tls_insecure_policy == "disallow": if tls_insecure_policy == "disallow":
tls_insecure_policy = None tls_insecure_policy = None
else: else:
tls_ca_cert = ( tls_ca_cert = tls_cert = tls_dest_ca_cert = tls_key = (
tls_cert tls_insecure_policy
) = tls_dest_ca_cert = tls_key = tls_insecure_policy = None ) = None
route = { route = {
"apiVersion": "route.openshift.io/v1", "apiVersion": "route.openshift.io/v1",

View File

@@ -0,0 +1,3 @@
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc

View File

@@ -0,0 +1,3 @@
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc

View File

@@ -8,14 +8,14 @@ install_command = pip install {opts} {packages}
[testenv:black] [testenv:black]
deps = deps =
black >= 23.0, < 24.0 black >= 25.0, < 26.0
commands = commands =
black {toxinidir}/plugins {toxinidir}/tests black {toxinidir}/plugins {toxinidir}/tests
[testenv:ansible-lint] [testenv:ansible-lint]
deps = deps =
ansible-lint >= 24.7.0 ansible-lint >= 25.1.2
changedir = {toxinidir} changedir = {toxinidir}
commands = commands =
ansible-lint ansible-lint
@@ -24,10 +24,12 @@ commands =
deps = deps =
flake8 flake8
{[testenv:black]deps} {[testenv:black]deps}
{[testenv:ansible-lint]deps}
commands = commands =
black -v --check --diff {toxinidir}/plugins {toxinidir}/tests black -v --check --diff {toxinidir}/plugins {toxinidir}/tests
flake8 {toxinidir} flake8 {toxinidir}
ansible-lint
[flake8] [flake8]
# E123, E125 skipped as they are invalid PEP-8. # E123, E125 skipped as they are invalid PEP-8.