From f877822c2dd155640062a442bba7d1addd4eb70b Mon Sep 17 00:00:00 2001 From: Lenka Segura Date: Feb 02 2023 06:53:12 +0000 Subject: [PATCH 1/4] Add tasks from communishift --- diff --git a/roles/fas2discourseconfig/defaults/main.yml b/roles/fas2discourseconfig/defaults/main.yml index 0bc6b5c..f6b8124 100644 --- a/roles/fas2discourseconfig/defaults/main.yml +++ b/roles/fas2discourseconfig/defaults/main.yml @@ -1,2 +1,6 @@ --- # defaults file for Fas2discourseConfig +fas2discourse_keytab_path: "/etc/fas2discourse" +fas2discourse_principal: "fas2discourse/fas2discourse.hostname@FEDORAPROJECT.ORG" +f2d_namespace: "fas2discourse-operator" +f2d_secret: "fas2discourse-operator-k8s-secret" diff --git a/roles/fas2discourseconfig/tasks/kerberos_auth.yml b/roles/fas2discourseconfig/tasks/kerberos_auth.yml new file mode 100644 index 0000000..3b834dd --- /dev/null +++ b/roles/fas2discourseconfig/tasks/kerberos_auth.yml @@ -0,0 +1,4 @@ +--- +# tasks file for Fas2Discourse +- name: Authorize with kinit + shell: kinit -k -t "{{ fas2discourse_keytab_path }}/fas2discourse-keytab" -l 3600 {{ fas2discourse_principal }} diff --git a/roles/fas2discourseconfig/tasks/main.yml b/roles/fas2discourseconfig/tasks/main.yml index 1a10545..ee885de 100644 --- a/roles/fas2discourseconfig/tasks/main.yml +++ b/roles/fas2discourseconfig/tasks/main.yml @@ -1,2 +1,6 @@ --- # tasks file for Fas2discourseConfig + +- include_tasks: kerberos_auth.yml +- include_tasks: retrieve_ipa_groups.yml +- include_tasks: sync_group_membership.yml diff --git a/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml b/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml new file mode 100644 index 0000000..11fb50f --- /dev/null +++ b/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml @@ -0,0 +1,20 @@ +--- +# tasks file for fas2discourse + +- name: Retrieve fasjson group/user data based on pattern supplied + fas2discourse_retrieve_fasjson_data: + keytab_path: "{{ fas2discourse_keytab_path }}" + principal: "{{ fas2discourse_principal }}" + group_name_pattern: "{{ fas2discourse_group_regex_pattern }}" + register: fasjson_response + +- name: Set fact + set_fact: + fas2discourse_fasjson_response: "{{ fasjson_response }}" +#- name: Prints the groups +# ansible.builtin.debug: +# msg: +# - "Groups found: {{ fas2discourse_fasjson_response['matched_groups'] }}" + +#- name: Write groups to json file +# copy: content="{{ fas2discourse_fasjson_response['matched_groups'] }}" dest=/tmp/groups.json diff --git a/roles/fas2discourseconfig/tasks/sync_group_membership.yml b/roles/fas2discourseconfig/tasks/sync_group_membership.yml new file mode 100644 index 0000000..6de3990 --- /dev/null +++ b/roles/fas2discourseconfig/tasks/sync_group_membership.yml @@ -0,0 +1,29 @@ +--- +# tasks file for Fas2Discourse + +- k8s_info: + api_version: v1 + kind: Secret + namespace: "{{ cao_namespace }}" + name: "{{ cao_secret }}" + register: cao_secret_yaml + +- set_fact: + cao_secret: "{{ cao_secret_yaml.resources[0] | from_yaml }}" +- set_fact: + fas2discourse_ocp_api_token: "{{ cao_secret.data['fas2discourse-ocp-api-token'] | b64decode }}" +- set_fact: + fas2discourse_ocp_api_host: "{{ cao_secret.data['fas2discourse-ocp-api-host'] | b64decode }}" + +- name: Sync user membership between IPA and Openshift Groups + community.okd.k8s: + api_key: "{{ fas2discourse_ocp_api_token }}" + host: "{{ fas2discourse_ocp_api_host }}" + state: present + definition: + apiVersion: user.openshift.io/v1 + kind: Group + metadata: + name: "{{ item['groupname'] }}-admins" + users: "{{ item['groupmembers'] | default([]) }}" + with_items: "{{ fas2discourse_fasjson_response['matched_groups'] }}" From 8f5ae589948cc9ee0a85eb85da07cc9e19bc8fb5 Mon Sep 17 00:00:00 2001 From: Lenka Segura Date: Feb 07 2023 09:50:00 +0000 Subject: [PATCH 2/4] Add python module to library --- diff --git a/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py b/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py new file mode 100644 index 0000000..e98aaeb --- /dev/null +++ b/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py @@ -0,0 +1,190 @@ +#!/usr/bin/python + +# Copyright: (c) 2018, Terry Jones +# GNU General Public License v3.0+ (see COPYING or +# https://www.gnu.org/licenses/gpl-3.0.txt) + +import json +import requests + +from ansible.module_utils.basic import AnsibleModule +from requests_kerberos import HTTPKerberosAuth + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: fas2discourse_retrieve_fasjson_data + +short_description: Retrieve fasjson group and user data. + +version_added: "0.0.1" + +description: This module retrieves fasjson group and users data which match +the groups in discourse. + +options: + keytab_path: + description: This is the location on disk where the kerberos keytab is stored. + required: true + type: str + principal: + description: The is the kerberos principal. + required: true + type: str + discourse_groups: + description: These are the groups retrieved from the discouse. + required: true + type: str + +author: + - David Kirwan (dkirwan@redhat.com) + - Lenka Segura (lsegura@redhat.com) + - Patrik Polakovic (ppolakov@redhat.com) +""" + +EXAMPLES = r""" +- name: Retrieve fasjson group/user data based on the discourse groups + fas2discourse_retrieve_fasjson_data: + keytab_path: "{{ fas2discourse_keytab }}" + principal: "{{ fas2discourse_principal }}" + discourse_groups: "{{ discourse_groups }}" + register: fas2discourse_fasjson_response +""" + +RETURN = r""" +# These are examples of possible return values, and in general should use other names for return +# values. +matched_groups: + description: The groups and their users which match the discourse groups. + type: str + returned: always + sample: +[ + { + "group_name": "yyy", + "group_members": [ + "uuu", + "iii", + "eee", + "zqq" + ] + }, + { + "group_name": "abc", + "group_members": [ + "xyc", + "cyz", + "xxx", + "zzz" + ] + } +] + +msg: + description: The output message that the module generates. + type: str + returned: always + sample: 'Successfully retrieved groups and their users from fasjson.' +""" + + +# Helper function to create the http requests +def get_http_client(keytab_path, principal): + try: + kerberos_auth = HTTPKerberosAuth(principal=principal) + except Exception as e: + print("Error trying to authenticate with Kerberos", e) + raise + session = requests.Session() + session.auth = kerberos_auth + return session + + +# Helper function to retrieve fasjson groups +def get_groups(http_client): + response = http_client.get("%sgroups/" % ("https://fasjson.fedoraproject.org/v1/")) + if response.ok: + return response.json() + + +# Helper function to retrieve fasjson group members +def get_group_members(http_client, groupname): + response = http_client.get( + "%sgroups/%s/members/" % ("https://fasjson.fedoraproject.org/v1/", groupname) + ) + if response.ok: + return response.json() + + +def run_module(): + # define available arguments/parameters a user can pass to the module + module_args = dict( + keytab_path=dict(type="str", required=True), + principal=dict(type="str", required=True), + discourse_groups=dict(type="str", required=True), + ) + + # seed the result dict in the object + # we primarily care about changed and state + # changed is if this module effectively modified the target + # state will include any data that you want your module to pass back + # for consumption, for example, in a subsequent task + result = dict(changed=False, original_message="", message="") + + # the AnsibleModule object will be our abstraction working with Ansible + # this includes instantiation, a couple of common attr would be the + # args/params passed to the execution, as well as if the module + # supports check mode + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + # if the user is working with this module in only check mode we do not + # want to make any changes to the environment, just return the current + # state with no modifications + if module.check_mode: + module.exit_json(**result) + + # manipulate or modify the state as needed (this is going to be the + # part where your module will do what it needs to do) + keytab_path = module.params["keytab_path"] + principal = module.params["principal"] +# discourse_groups = module.params["discourse_groups"] + # let's hardcode the discourse groups for the moment + discourse_groups = ["council", "moderators", "design", "magazine_editors", "community-blog", "pgm-team"] + + try: + http_client = get_http_client(keytab_path, principal) + groups_response = get_groups(http_client) + + fas2discourse_groups = [] + for v in groups_response["result"]: + if v["groupname"] in discourse_groups: + group = {"groupname": v["groupname"], "groupmembers": []} + + group_member_res = get_group_members(http_client, v["groupname"]) + # print(json.dumps(group_member_res)) + + for v in group_member_res["result"]: + group["groupmembers"].append(v["username"]) + fas2discourse_groups.append(group) + # print(v["groupname"]) + + # print(json.dumps(fas2discourse_groups)) + + result["matched_groups"] = json.dumps(fas2discourse_groups) + result["changed"] = True + result["msg"] = "Successfully retrieved groups and their users from fasjson." + except Exception: + raise + + # in the event of a successful module execution, you will want to + # simple AnsibleModule.exit_json(), passing the key/value results + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() From be634d90338f91167b4e653fa84e91db9d14405c Mon Sep 17 00:00:00 2001 From: Lenka Segura Date: Feb 09 2023 15:38:31 +0000 Subject: [PATCH 3/4] Add python module for retrieving discourse groups --- diff --git a/roles/fas2discourseconfig/defaults/main.yml b/roles/fas2discourseconfig/defaults/main.yml index f6b8124..78d7adf 100644 --- a/roles/fas2discourseconfig/defaults/main.yml +++ b/roles/fas2discourseconfig/defaults/main.yml @@ -2,5 +2,6 @@ # defaults file for Fas2discourseConfig fas2discourse_keytab_path: "/etc/fas2discourse" fas2discourse_principal: "fas2discourse/fas2discourse.hostname@FEDORAPROJECT.ORG" +discourse_groups: {{ discourse_groups }} f2d_namespace: "fas2discourse-operator" f2d_secret: "fas2discourse-operator-k8s-secret" diff --git a/roles/fas2discourseconfig/library/fas2discourse_retrieve_discourse_data.py b/roles/fas2discourseconfig/library/fas2discourse_retrieve_discourse_data.py new file mode 100644 index 0000000..6678ebe --- /dev/null +++ b/roles/fas2discourseconfig/library/fas2discourse_retrieve_discourse_data.py @@ -0,0 +1,126 @@ +#!/usr/bin/python + +# Copyright: (c) 2018, Terry Jones +# GNU General Public License v3.0+ (see COPYING or +# https://www.gnu.org/licenses/gpl-3.0.txt) + +import json +import requests + +from ansible.module_utils.basic import AnsibleModule +from requests_kerberos import HTTPKerberosAuth + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: fas2discourse_retrieve_discourse_data + +short_description: Retrieve discourse groups. + +version_added: "0.0.1" + +description: This module retrieves discourse groups. + +options: + discourse_groups: + description: These are the groups retrieved from the discouse. + required: true + type: str + +author: + - David Kirwan (dkirwan@redhat.com) + - Lenka Segura (lsegura@redhat.com) + - Patrik Polakovic (ppolakov@redhat.com) +""" + +EXAMPLES = r""" +- name: Retrieve discourse groups. + fas2discourse_retrieve_discourse_data: + discourse_groups: "{{ discourse_groups }}" + register: fas2discourse_discourse_response +""" + +RETURN = r""" +# These are examples of possible return values, and in general should use other names for return +# values. + +["group1", "group2", "group3"] + + +msg: + description: The output message that the module generates. + type: str + returned: always + sample: 'Successfully retrieved groups from Dicourse.' +""" + + +# Helper function to create the http requests +def get_http_client(): + session = requests.Session() + return session + + +# Helper function to retrieve fasjson groups +def get_groups(http_client): + response = http_client.get("%sgroups.json" % ("https://discussion.fedoraproject.org/")) + if response.ok: + return response.json() + + +def run_module(): + # define available arguments/parameters a user can pass to the module + module_args = dict( + discourse_groups=list(type="str", required=True), + ) + + # seed the result dict in the object + # we primarily care about changed and state + # changed is if this module effectively modified the target + # state will include any data that you want your module to pass back + # for consumption, for example, in a subsequent task + result = dict(changed=False, original_message="", message="") + + # the AnsibleModule object will be our abstraction working with Ansible + # this includes instantiation, a couple of common attr would be the + # args/params passed to the execution, as well as if the module + # supports check mode + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + # if the user is working with this module in only check mode we do not + # want to make any changes to the environment, just return the current + # state with no modifications + if module.check_mode: + module.exit_json(**result) + + # manipulate or modify the state as needed (this is going to be the + # part where your module will do what it needs to do) + discourse_api = module.params["discourse_api"] + + try: + http_client = get_http_client() + groups_response = get_groups(http_client) + + discourse_groups = [] + for v in groups_response["groups"]: + dicourse_groups.append(v["name"]) + + + result["discourse_groups"] = json.dumps(discourse_groups) + result["changed"] = True + result["msg"] = "Successfully retrieved groups from Discourse." + except Exception: + raise + + # in the event of a successful module execution, you will want to + # simple AnsibleModule.exit_json(), passing the key/value results + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py b/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py index e98aaeb..3cef472 100644 --- a/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py +++ b/roles/fas2discourseconfig/library/fas2discourse_retrieve_fasjson_data.py @@ -33,7 +33,7 @@ options: required: true type: str discourse_groups: - description: These are the groups retrieved from the discouse. + description: These are the groups retrieved from the discourse. required: true type: str diff --git a/roles/fas2discourseconfig/tasks/main.yml b/roles/fas2discourseconfig/tasks/main.yml index ee885de..64b36a3 100644 --- a/roles/fas2discourseconfig/tasks/main.yml +++ b/roles/fas2discourseconfig/tasks/main.yml @@ -1,6 +1,7 @@ --- # tasks file for Fas2discourseConfig +- include tasks: retrieve_discourse_groups.yml - include_tasks: kerberos_auth.yml - include_tasks: retrieve_ipa_groups.yml - include_tasks: sync_group_membership.yml diff --git a/roles/fas2discourseconfig/tasks/retrieve_discourse_groups.yml b/roles/fas2discourseconfig/tasks/retrieve_discourse_groups.yml new file mode 100644 index 0000000..85bf322 --- /dev/null +++ b/roles/fas2discourseconfig/tasks/retrieve_discourse_groups.yml @@ -0,0 +1,18 @@ +--- +# tasks file for fas2discourse + +- name: Retrieve discourse groups + fas2discourse_retrieve_discourse_data: + register: discourse_response + +- name: Set fact + set_fact: + fas2discourse_discourse_response: "{{ discourse_response }}" + +- name: Prints the groups + ansible.builtin.debug: + msg: + - "Groups found: {{ fas2discourse_discourse_response['matched_groups'] }}" + +- name: Write groups to json file + copy: content="{{ fas2discourse_discourse_response['matched_groups'] }}" dest=/tmp/groups.json diff --git a/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml b/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml index 11fb50f..bac9b16 100644 --- a/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml +++ b/roles/fas2discourseconfig/tasks/retrieve_ipa_groups.yml @@ -5,16 +5,17 @@ fas2discourse_retrieve_fasjson_data: keytab_path: "{{ fas2discourse_keytab_path }}" principal: "{{ fas2discourse_principal }}" - group_name_pattern: "{{ fas2discourse_group_regex_pattern }}" + discourse_groups: "{{ discourse_groups }}" register: fasjson_response - name: Set fact set_fact: fas2discourse_fasjson_response: "{{ fasjson_response }}" -#- name: Prints the groups -# ansible.builtin.debug: -# msg: -# - "Groups found: {{ fas2discourse_fasjson_response['matched_groups'] }}" -#- name: Write groups to json file -# copy: content="{{ fas2discourse_fasjson_response['matched_groups'] }}" dest=/tmp/groups.json +- name: Prints the groups + ansible.builtin.debug: + msg: + - "Groups found: {{ fas2discourse_fasjson_response }}" + +- name: Write groups to json file + copy: content="{{ fas2discourse_fasjson_response }}" dest=/tmp/fasjson_groups.json diff --git a/roles/fas2discourseconfig/tasks/sync_group_membership.yml b/roles/fas2discourseconfig/tasks/sync_group_membership.yml index 6de3990..bdc112e 100644 --- a/roles/fas2discourseconfig/tasks/sync_group_membership.yml +++ b/roles/fas2discourseconfig/tasks/sync_group_membership.yml @@ -4,18 +4,19 @@ - k8s_info: api_version: v1 kind: Secret - namespace: "{{ cao_namespace }}" - name: "{{ cao_secret }}" - register: cao_secret_yaml + namespace: "{{ f2d_namespace }}" + name: "{{ f2d_secret }}" + register: f2d_secret_yaml - set_fact: - cao_secret: "{{ cao_secret_yaml.resources[0] | from_yaml }}" + cao_secret: "{{ f2d_secret_yaml.resources[0] | from_yaml }}" - set_fact: - fas2discourse_ocp_api_token: "{{ cao_secret.data['fas2discourse-ocp-api-token'] | b64decode }}" + fas2discourse_ocp_api_token: "{{ f2d_secret.data['fas2discourse-ocp-api-token'] | b64decode }}" - set_fact: - fas2discourse_ocp_api_host: "{{ cao_secret.data['fas2discourse-ocp-api-host'] | b64decode }}" + fas2discourse_ocp_api_host: "{{ f2d_secret.data['fas2discourse-ocp-api-host'] | b64decode }}" -- name: Sync user membership between IPA and Openshift Groups +- name: Sync user membership between IPA and Discourse Groups + # figure out how to do this with discourse community.okd.k8s: api_key: "{{ fas2discourse_ocp_api_token }}" host: "{{ fas2discourse_ocp_api_host }}" @@ -24,6 +25,6 @@ apiVersion: user.openshift.io/v1 kind: Group metadata: - name: "{{ item['groupname'] }}-admins" + name: "{{ item['groupname'] }}" users: "{{ item['groupmembers'] | default([]) }}" with_items: "{{ fas2discourse_fasjson_response['matched_groups'] }}" From 5f3a71d16385eb1af46dd62f1188413c35ad7c97 Mon Sep 17 00:00:00 2001 From: Lenka Segura Date: Feb 15 2023 12:31:04 +0000 Subject: [PATCH 4/4] Add kerberos and dependencies to Dockerfile --- diff --git a/Dockerfile b/Dockerfile index 0b517f8..9efcbdb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,15 @@ FROM quay.io/operator-framework/ansible-operator:v1.23.0 +USER root +RUN dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm +RUN dnf install -y gcc python3-devel python38-devel krb5-devel krb5-workstation python3-setuptools python3-pip python3-psycopg2 COPY requirements.yml ${HOME}/requirements.yml RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ && chmod -R ug+rwx ${HOME}/.ansible +RUN pip3.8 install --no-cache-dir --upgrade pip wheel +RUN pip3.8 install requests-kerberos + COPY watches.yaml ${HOME}/watches.yaml COPY roles/ ${HOME}/roles/ COPY playbooks/ ${HOME}/playbooks/ diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index ce65302..efe4b94 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,13 +5,12 @@ generatorOptions: disableNameSuffixHash: true configMapGenerator: -- name: manager-config - files: +- files: - controller_manager_config.yaml name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - - name: controller - newName: quay.io/fedora/fas2discourse-operator - newTag: v.0.0.1 +- name: controller + newName: quay.io/fedora/fas2discourse-operator + newTag: v0.0.8 diff --git a/roles/fas2discourseconfig/tasks/main.yml b/roles/fas2discourseconfig/tasks/main.yml index 64b36a3..0ae01f9 100644 --- a/roles/fas2discourseconfig/tasks/main.yml +++ b/roles/fas2discourseconfig/tasks/main.yml @@ -1,7 +1,7 @@ --- # tasks file for Fas2discourseConfig -- include tasks: retrieve_discourse_groups.yml +- include_tasks: retrieve_discourse_groups.yml - include_tasks: kerberos_auth.yml - include_tasks: retrieve_ipa_groups.yml - include_tasks: sync_group_membership.yml