Skip to content

Commit

Permalink
Merge branch 'main' into docs-mwtt
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex-Izquierdo authored Oct 30, 2023
2 parents 6f4afda + 2a78982 commit 21a3dce
Show file tree
Hide file tree
Showing 50 changed files with 3,142 additions and 1,172 deletions.
6 changes: 3 additions & 3 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[bumpversion]
current_version = 1.0.2
commit = True
tag = True
current_version = 1.0.3
commit = False
tag = False
search = {current_version}

[bumpversion:file:setup.cfg]
Expand Down
23 changes: 23 additions & 0 deletions .github/actions/image-test/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: build-and-test-image
description: Build and test the container image

runs:
using: composite
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build container image
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64
tags: localhost/ansible-rulebook:test
load: true

- name: Run tests
shell: bash
run: >
docker run --rm -u 0 localhost/ansible-rulebook:test bash -c '
pip install -r requirements_test.txt &&
pytest -m "e2e" -n auto'
10 changes: 2 additions & 8 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,8 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: Build local image
run: docker build -t localhost/ansible-rulebook:test .

- name: Run tests
run: >
docker run --rm -u 0 localhost/ansible-rulebook:test bash -c '
pip install -r requirements_test.txt &&
pytest -m "e2e" -n auto'
- name: Build and test the container image
uses: ./.github/actions/image-test

build-and-push-image:
needs: build-and-test-image
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,13 @@ jobs:
flags: "unittests-${{ matrix.python-version }}"
name: codecov-umbrella
verbose: true

build-and-test-image:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Build and test the container image
uses: ./.github/actions/image-test
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ repos:
rev: v9.5.0
hooks:
- id: commitlint
additional_dependencies: ['@commitlint/config-conventional']
stages: [commit-msg]
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@
## [Unreleased]

### Added
- support for firing multiple rules

### Fixed
- Job_template and workflow_template actions honor custom hosts limit

### Removed

## [1.0.3] - 2023-10-17

### Added
- support for firing multiple rules

### Fixed
- bug fix in run_workflow_template


### Removed
## [1.0.2] - 2023-08-14

### Added
Expand Down
2 changes: 1 addition & 1 deletion ansible_rulebook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@

"""Top-level package for Ansible Events."""

__version__ = "1.0.2"
__version__ = "1.0.3"
Empty file.
54 changes: 54 additions & 0 deletions ansible_rulebook/action/control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import asyncio
from dataclasses import dataclass
from typing import List


@dataclass(frozen=True)
class Control:
"""Control information when running an action
Attributes:
queue: asyncio.Queue
This is the queue on which we would be sending action status
periodically when the action is running
inventory: str
This is the inventory information from the command line
It currently is the data that is read from a file, in the future
it could be a directory or an inventory name from the controller
hosts: list[str]
The list of servers passed into ansible-playbook or controller
variables: dict
The variables passed in from the command line plus the matching event
data with event or events key.
project_data_file: str
This is the directory where the collection data is sent from the
AAP server over the websocket is untarred to. The collection could
contain the playbook that is used in the run_playbook action.
"""

__slots__ = [
"queue",
"inventory",
"hosts",
"variables",
"project_data_file",
]
queue: asyncio.Queue
inventory: str
hosts: List[str]
variables: dict
project_data_file: str
83 changes: 83 additions & 0 deletions ansible_rulebook/action/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import sys
from dataclasses import asdict
from pprint import pprint

import dpath
from drools import ruleset as lang

from ansible_rulebook.util import get_horizontal_rule

from .control import Control
from .helper import Helper
from .metadata import Metadata

logger = logging.getLogger(__name__)


class Debug:
"""The debug action tries to mimic the ansible debug task with optional
msg: Prints a message
var: Prints a variable
default: print the metadata, control information and facts from the
rule engine
At the end we send back the action status
"""

def __init__(self, metadata: Metadata, control: Control, **action_args):
self.helper = Helper(metadata, control, "debug")
self.action_args = action_args

async def __call__(self):
if "msg" in self.action_args:
messages = self.action_args.get("msg")
if not isinstance(messages, list):
messages = [messages]
for msg in messages:
print(msg)
elif "var" in self.action_args:
key = self.action_args.get("var")
try:
print(
dpath.get(
self.helper.control.variables, key, separator="."
)
)
except KeyError:
logger.error("Key %s not found in variable pool", key)
raise
else:
print(get_horizontal_rule("="))
print("kwargs:")
args = asdict(self.helper.metadata)
project_data_file = self.helper.control.project_data_file
args.update(
{
"inventory": self.helper.control.inventory,
"hosts": self.helper.control.hosts,
"variables": self.helper.control.variables,
"project_data_file": project_data_file,
}
)
pprint(args)
print(get_horizontal_rule("="))
print("facts:")
pprint(lang.get_facts(self.helper.metadata.rule_set))
print(get_horizontal_rule("="))

sys.stdout.flush()
await self.helper.send_default_status()
143 changes: 143 additions & 0 deletions ansible_rulebook/action/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import uuid
from typing import Dict

from ansible_rulebook.conf import settings
from ansible_rulebook.event_filter.insert_meta_info import main as insert_meta
from ansible_rulebook.util import run_at

from .control import Control
from .metadata import Metadata

KEY_EDA_VARS = "ansible_eda"
INTERNAL_ACTION_STATUS = "successful"


class Helper:
"""
Helper class stores the metadata, the control attributes and has
methods to send data to the Queue.
Attributes
----------
metadata : Metadata
a data class that stores rule specific data
control : Control
a control dataclass that stores the runtime information about
the queue on which we send the status for the action, the inventory
information, the hosts data and the variables that we would like
to pass into the action
uuid : str
each action has a uuid that is generated to track it
action : str
the name of the action, set by the sub classe
Methods
-------
send_status(data={}, obj_type:"action")
Sends the action status information on the queue
send_default_status()
Sends the default action status, used mostly with internal
actions like debug, print_event, set_fact, retract_fact,
noop, post_event
get_events()
Fetches the matching events from the variables
collect_extra_vars()
Create extra_vars to be sent to playbook and job template which
includes rule and matching events.
embellish_internal_event()
Add internal sources for facts and events posted from inside of
a rulebook
"""

def __init__(self, metadata: Metadata, control: Control, action: str):
self.metadata = metadata
self.control = control
self.uuid = str(uuid.uuid4())
self.action = action

async def send_status(self, data: Dict, obj_type: str = "Action") -> None:
"""Send Action status information on the queue"""
payload = {
"type": obj_type,
"action": self.action,
"action_uuid": self.uuid,
"ruleset": self.metadata.rule_set,
"ruleset_uuid": self.metadata.rule_set_uuid,
"rule": self.metadata.rule,
"rule_uuid": self.metadata.rule_uuid,
"rule_run_at": self.metadata.rule_run_at,
"activation_id": settings.identifier,
"activation_instance_id": settings.identifier,
}
payload.update(data)
await self.control.queue.put(payload)

async def send_default_status(self):
"""Send default action status information on the queue"""
await self.send_status(
{
"run_at": run_at(),
"status": INTERNAL_ACTION_STATUS,
"matching_events": self.get_events(),
}
)

def get_events(self) -> Dict:
"""From the control variables, detect if its a single event
match or a multi event match and return a dictionary with
the event data with
m key for single event stored in the event key
m_0,m_1,.... for multiple matching events stored in
the events key
"""
if "event" in self.control.variables:
return {"m": self.control.variables["event"]}
if "events" in self.control.variables:
return self.control.variables["events"]
return {}

def embellish_internal_event(self, event: Dict) -> Dict:
"""Insert metadata for every internally generated event"""
return insert_meta(
event, **{"source_name": self.action, "source_type": "internal"}
)

def set_action(self, action) -> None:
self.action = action

def collect_extra_vars(self, user_extra_vars: Dict) -> Dict:
"""When we send information to ansible-playbook or job template
on AWX, we need the rule and event specific information to
be sent to this external process
the caller passes in the user_extra_vars from the action args
and then we append eda specific vars and return that as a
the updated dictionary that is sent to the external process
"""
extra_vars = user_extra_vars.copy() if user_extra_vars else {}

eda_vars = {
"ruleset": self.metadata.rule_set,
"rule": self.metadata.rule,
}
if "events" in self.control.variables:
eda_vars["events"] = self.control.variables["events"]
if "event" in self.control.variables:
eda_vars["event"] = self.control.variables["event"]

extra_vars[KEY_EDA_VARS] = eda_vars
return extra_vars
Loading

0 comments on commit 21a3dce

Please sign in to comment.