diff --git a/ansible/configs/namespace/README.adoc b/ansible/configs/namespace/README.adoc new file mode 100644 index 00000000000..71772393e61 --- /dev/null +++ b/ansible/configs/namespace/README.adoc @@ -0,0 +1,51 @@ += Namespace Configuration + +The `namespace` configuration is designed to deploy OpenShift workloads directly to existing namespaces without requiring infrastructure provisioning or bastion hosts. This is ideal for deploying applications to shared clusters or when you only have namespace-level access. + +== Features + +* No infrastructure provisioning required +* Direct connection to OpenShift clusters +* Support for both token and user/password authentication +* Simplified deployment for namespace-scoped workloads +* No SSH or bastion host dependencies + +== Usage + +1. Copy `sample_vars.yml` to create your deployment variables file +2. Configure your OpenShift connection details +3. Define the workloads to deploy in `ocp_workloads_namespaced` +4. Run the deployment + +=== Example Deployment + +[source,bash] +---- + ansible-navigator run ansible/main.yml -e @ansible/configs/namespace/sample_vars.yml -e @/secrets/ns.yaml -e ACTION=create +---- + +== Required Variables + +* `sandbox_openshift_api_url`: OpenShift API endpoint +* `sandbox_openshift_namespace`: Target namespace +* Authentication (choose one): + ** `sandbox_openshift_api_token`: API token + ** `sandbox_openshift_user` + `sandbox_openshift_password`: Username/password + +== Optional Variables + +* `sandbox_openshift_cluster`: Cluster name for identification +* `sandbox_openshift_apps_domain`: Apps domain for route creation +* `sandbox_openshift_credentials`: Additional credential objects + +== Workloads + +Define workloads in the `ocp_workloads_namespaced` list. Each workload should be a role that can deploy to OpenShift using the provided connection variables. + +Example: +[source,yaml] +---- +ocp_workloads_namespaced: + - ocp_workloads_namespaced_example + - my_custom_workload +---- diff --git a/ansible/configs/namespace/default_vars.yml b/ansible/configs/namespace/default_vars.yml new file mode 100644 index 00000000000..17f906f7643 --- /dev/null +++ b/ansible/configs/namespace/default_vars.yml @@ -0,0 +1,7 @@ +--- +# Namespace Config Default Variables +# This config deploys OpenShift workloads directly to a namespace without +# requiring infrastructure + +# List of namespace workloads to deploy +ocp_workloads_namespaced: [] diff --git a/ansible/configs/namespace/destroy_env.yml b/ansible/configs/namespace/destroy_env.yml new file mode 100644 index 00000000000..650b18ccba2 --- /dev/null +++ b/ansible/configs/namespace/destroy_env.yml @@ -0,0 +1,37 @@ +--- +- name: Destroy Namespaced OCP Workloads + hosts: localhost + gather_facts: false + become: false + tags: + - ocp_workloads_namespaced + tasks: + - name: Destroy namespaced workloads + when: + - ocp_workloads_namespaced | default([]) | length > 0 + block: + - name: Remove namespaced workload "{{ workload_loop_var }}" + ansible.builtin.include_role: + name: "{{ workload_loop_var }}" + vars: + ACTION: "destroy" + # Pass through OpenShift connection variables + sandbox_openshift_cluster: "{{ sandbox_openshift_cluster | default('') }}" + sandbox_openshift_api_url: "{{ sandbox_openshift_api_url | default('') }}" + sandbox_openshift_apps_domain: "{{ sandbox_openshift_apps_domain | default('') }}" + sandbox_openshift_namespace: "{{ sandbox_openshift_namespace | default('') }}" + sandbox_openshift_api_token: "{{ sandbox_openshift_api_token | default('') }}" + sandbox_openshift_credentials: "{{ sandbox_openshift_credentials | default([]) }}" + sandbox_openshift_user: "{{ sandbox_openshift_user | default('') }}" + sandbox_openshift_password: "{{ sandbox_openshift_password | default('') }}" + loop: "{{ ocp_workloads_namespaced }}" + loop_control: + loop_var: workload_loop_var + +- name: Destroy complete + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Namespace workloads destroyed successfully" diff --git a/ansible/configs/namespace/lifecycle.yml b/ansible/configs/namespace/lifecycle.yml new file mode 100644 index 00000000000..9192d962cbe --- /dev/null +++ b/ansible/configs/namespace/lifecycle.yml @@ -0,0 +1,30 @@ +--- +- name: Build inventory + ansible.builtin.import_playbook: post_infra.yml + +- name: Destroy workload(s) + hosts: ocp_bastions + become: false + gather_facts: false + tags: + - step005 + tasks: + - name: Fail if no action defined + when: ACTION is not defined or ACTION == '' + ansible.builtin.fail: + msg: ACTION must be defined + + - name: Set facts for OpenShift cluster(s) + include_tasks: set_cluster_facts.yml + + - name: Run Workloads to perform {{ ACTION }} + loop: "{{ cluster_workloads }}" + loop_control: + loop_var: __workload + label: "{{ __workload.name }}" + include_tasks: run_workload_on_clusters.yml + vars: + ACTION: destroy + +- name: Cleanup + ansible.builtin.import_playbook: cleanup.yml diff --git a/ansible/configs/namespace/post_infra.yml b/ansible/configs/namespace/post_infra.yml new file mode 100644 index 00000000000..dad60fdf180 --- /dev/null +++ b/ansible/configs/namespace/post_infra.yml @@ -0,0 +1,8 @@ +--- +- name: Step 002 Post Infrastructure + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Step 002 Post Infrastructure - No infrastructure setup needed for namespace config" diff --git a/ansible/configs/namespace/post_software.yml b/ansible/configs/namespace/post_software.yml new file mode 100644 index 00000000000..4c506386244 --- /dev/null +++ b/ansible/configs/namespace/post_software.yml @@ -0,0 +1,8 @@ +--- +- name: Step 005 Post Software + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Step 005 Post Software - Namespace workloads deployment completed" diff --git a/ansible/configs/namespace/pre_infra.yml b/ansible/configs/namespace/pre_infra.yml new file mode 100644 index 00000000000..5c7079c0536 --- /dev/null +++ b/ansible/configs/namespace/pre_infra.yml @@ -0,0 +1,8 @@ +--- +- name: Step 000 Pre Infrastructure + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Step 000 Pre Infrastructure - No infrastructure needed for namespace-only deployment" diff --git a/ansible/configs/namespace/pre_software.yml b/ansible/configs/namespace/pre_software.yml new file mode 100644 index 00000000000..fb2072b6f34 --- /dev/null +++ b/ansible/configs/namespace/pre_software.yml @@ -0,0 +1,8 @@ +--- +- name: Step 003 Pre Software + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Step 003 Pre Software - No infrastructure software needed" diff --git a/ansible/configs/namespace/requirements.yml b/ansible/configs/namespace/requirements.yml new file mode 100644 index 00000000000..81d2d8ebc40 --- /dev/null +++ b/ansible/configs/namespace/requirements.yml @@ -0,0 +1,4 @@ +--- +collections: +- name: kubernetes.core + version: 5.0.0 diff --git a/ansible/configs/namespace/sample_vars.yml b/ansible/configs/namespace/sample_vars.yml new file mode 100644 index 00000000000..390f3cbbeec --- /dev/null +++ b/ansible/configs/namespace/sample_vars.yml @@ -0,0 +1,15 @@ +--- +# Sample Variables for Namespace Config +# Copy this file and customize for your deployment + +# Basic environment information +cloud_provider: none +env_type: namespace +guid: test-tt + +# List of workloads to deploy +ocp_workloads_namespaced: + - ocp_workloads_namespaced_example + +# Output directory for deployment artifacts +output_dir: "/tmp/output_dir/{{ guid }}" diff --git a/ansible/configs/namespace/software.yml b/ansible/configs/namespace/software.yml new file mode 100644 index 00000000000..b00c0ed694d --- /dev/null +++ b/ansible/configs/namespace/software.yml @@ -0,0 +1,48 @@ +--- +- name: Step 004 Environment specific Software + hosts: localhost + gather_facts: false + become: false + tasks: + - debug: + msg: "Software {{ ACTION | default('provision') }} started for namespace workloads" + +- name: Deploy Namespaced OCP Workloads + hosts: localhost + gather_facts: false + become: false + tags: + - ocp_workloads_namespaced + tasks: + - name: Deploy namespaced workloads + when: + - ocp_workloads_namespaced | default([]) | length > 0 + block: + - name: Apply namespaced workload "{{ workload_loop_var }}" + ansible.builtin.include_role: + name: "{{ workload_loop_var }}" + vars: + ACTION: "{{ ACTION | default('provision') }}" + # Pass through OpenShift connection variables + sandbox_openshift_cluster: "{{ sandbox_openshift_cluster | default('') }}" + sandbox_openshift_api_url: "{{ sandbox_openshift_api_url | default('') }}" + sandbox_openshift_apps_domain: "{{ sandbox_openshift_apps_domain | default('') }}" + sandbox_openshift_namespace: "{{ sandbox_openshift_namespace | default('') }}" + sandbox_openshift_api_token: "{{ sandbox_openshift_api_token | default('') }}" + sandbox_openshift_credentials: "{{ sandbox_openshift_credentials | default([]) }}" + sandbox_openshift_user: "{{ sandbox_openshift_user | default('') }}" + sandbox_openshift_password: "{{ sandbox_openshift_password | default('') }}" + loop: "{{ ocp_workloads_namespaced }}" + loop_control: + loop_var: workload_loop_var + +- name: Software flight-check + hosts: localhost + connection: local + gather_facts: false + become: false + tags: + - post_flight_check + tasks: + - debug: + msg: "Software {{ ACTION | default('provision') }} completed successfully" diff --git a/ansible/roles/ocp_workloads_namespaced_example/defaults/main.yml b/ansible/roles/ocp_workloads_namespaced_example/defaults/main.yml new file mode 100644 index 00000000000..2548af7a0ba --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/defaults/main.yml @@ -0,0 +1,18 @@ +--- +become_override: false +silent: false + +# Default values for the namespaced example workload +ocp_workloads_namespaced_example_defaults: + app_name: "hello-world" + app_image: "quay.io/redhattraining/hello-world-nginx:v1.0" + app_replicas: 1 + service_port: 8080 + create_route: true + route_path: "/" + resource_limits: + cpu: "100m" + memory: "128Mi" + resource_requests: + cpu: "50m" + memory: "64Mi" diff --git a/ansible/roles/ocp_workloads_namespaced_example/readme.adoc b/ansible/roles/ocp_workloads_namespaced_example/readme.adoc new file mode 100644 index 00000000000..822469ca3c8 --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/readme.adoc @@ -0,0 +1,104 @@ += ocp_workloads_namespaced_example + +This role provides an example of how to create namespaced OpenShift workloads that can be deployed without requiring infrastructure provisioning or bastion hosts. + +== Overview + +This role deploys a simple nginx web server to an OpenShift namespace using a mixed approach that demonstrates both `oc` commands and `kubernetes.core` modules. It demonstrates: + +* **Hybrid deployment approach**: Shows both command-line and API-based methods +* **Authentication**: Using `oc login` for robust connectivity +* **Resource creation**: Mix of `kubernetes.core.k8s` modules and `oc apply` commands +* **OpenShift-specific resources**: Routes created via `oc` commands +* **Standard Kubernetes resources**: Deployments and Services via kubernetes.core modules +* **Verification**: Using both `kubernetes.core.k8s_info` and `oc get` commands + +== Requirements + +* Access to an OpenShift cluster +* Either an API token or username/password credentials +* Target namespace must exist and be accessible +* Ansible kubernetes.core collection installed + +== Role Variables + +The role uses a dictionary `ocp_workloads_namespaced_example` with the following default values: + +[source,yaml] +---- +ocp_workloads_namespaced_example_defaults: + app_name: "hello-world" + app_image: "quay.io/openshifttestbed/hello-openshift:latest" + app_replicas: 1 + service_port: 8080 + create_route: true + route_path: "/" + resource_limits: + cpu: "100m" + memory: "128Mi" + resource_requests: + cpu: "50m" + memory: "64Mi" +---- + +== Connection Variables + +These variables are passed from the namespace config: + +* `sandbox_openshift_api_url`: OpenShift API endpoint (required) +* `sandbox_openshift_namespace`: Target namespace (required) +* `sandbox_openshift_api_token`: API token (required if not using user/password) +* `sandbox_openshift_user`: Username (required if not using token) +* `sandbox_openshift_password`: Password (required if not using token) + +== Usage Example + +In your namespace config variables: + +[source,yaml] +---- +ocp_workloads_namespaced: + - ocp_workloads_namespaced_example + +# Customize the workload +ocp_workloads_namespaced_example_vars: + app_name: "my-hello-app" + app_replicas: 2 + create_route: true +---- + +== Deployed Resources + +The role creates: + +1. **Deployment**: Runs the application pods +2. **Service**: Provides internal cluster access +3. **Route**: (Optional) Provides external access via OpenShift router + +== Authentication + +The role supports two authentication methods: + +1. **API Token**: Recommended for automation +2. **Username/Password**: For interactive use + +== Development + +This role serves as a template for creating other namespaced workloads. Key patterns demonstrated: + +=== Hybrid Approach Benefits + +* **oc commands**: Better for authentication, OpenShift-specific resources, and troubleshooting +* **kubernetes.core modules**: Better for structured resource definitions and Ansible integration +* **Mixed usage**: Combines the strengths of both approaches + +=== Implementation Patterns + +* Use `oc login` for initial authentication (more robust connectivity) +* Use `kubernetes.core.k8s` for standard Kubernetes resources (Deployments, Services) +* Use `oc apply/delete` for OpenShift-specific resources (Routes) +* Use `kubernetes.core.k8s_info` for detailed status checking +* Use `oc get` for quick verification and troubleshooting +* Implement proper error handling and retry logic for both approaches +* Support both token and username/password authentication +* Provide comprehensive status reporting showing which methods were used diff --git a/ansible/roles/ocp_workloads_namespaced_example/tasks/main.yml b/ansible/roles/ocp_workloads_namespaced_example/tasks/main.yml new file mode 100644 index 00000000000..03a4801b4c7 --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/tasks/main.yml @@ -0,0 +1,30 @@ +--- +# Do not modify this file + +- name: Running Pre Workload Tasks + include_tasks: + file: ./pre_workload.yml + apply: + become: "{{ become_override | bool }}" + when: ACTION == "create" or ACTION == "provision" + +- name: Running Workload Tasks + include_tasks: + file: ./workload.yml + apply: + become: "{{ become_override | bool }}" + when: ACTION == "create" or ACTION == "provision" + +- name: Running Post Workload Tasks + include_tasks: + file: ./post_workload.yml + apply: + become: "{{ become_override | bool }}" + when: ACTION == "create" or ACTION == "provision" + +- name: Running Workload removal Tasks + include_tasks: + file: ./remove_workload.yml + apply: + become: "{{ become_override | bool }}" + when: ACTION == "destroy" or ACTION == "remove" diff --git a/ansible/roles/ocp_workloads_namespaced_example/tasks/post_workload.yml b/ansible/roles/ocp_workloads_namespaced_example/tasks/post_workload.yml new file mode 100644 index 00000000000..d467db3b3d9 --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/tasks/post_workload.yml @@ -0,0 +1,49 @@ +--- +# Post-workload tasks for namespaced example + +- name: Verify deployment is ready + kubernetes.core.k8s_info: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: apps/v1 + kind: Deployment + name: "{{ ocp_workloads_namespaced_example.app_name }}" + namespace: "{{ sandbox_openshift_namespace }}" + wait: true + wait_condition: + type: Available + status: "True" + wait_timeout: 300 + register: deployment_status + +- name: Get route URL if route was created + when: ocp_workloads_namespaced_example.create_route | bool + kubernetes.core.k8s_info: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: route.openshift.io/v1 + kind: Route + name: "{{ ocp_workloads_namespaced_example.app_name }}-route" + namespace: "{{ sandbox_openshift_namespace }}" + register: route_info + +- name: Display application access information + debug: + msg: + - "Application '{{ ocp_workloads_namespaced_example.app_name }}' deployed successfully!" + - "Namespace: {{ sandbox_openshift_namespace }}" + - "Deployment status: {{ deployment_status.resources[0].status.conditions | selectattr('type', 'equalto', 'Available') | map(attribute='status') | first | default('Unknown') }}" + - "Ready replicas: {{ deployment_status.resources[0].status.readyReplicas | default(0) }}/{{ ocp_workloads_namespaced_example.app_replicas }}" + - "{% if ocp_workloads_namespaced_example.create_route and route_info.resources | length > 0 %}Application URL: http://{{ route_info.resources[0].spec.host }}{% endif %}" + when: not silent | bool + +- name: post_workload tasks complete + debug: + msg: "Post-Workload tasks completed successfully." + when: not silent | bool diff --git a/ansible/roles/ocp_workloads_namespaced_example/tasks/pre_workload.yml b/ansible/roles/ocp_workloads_namespaced_example/tasks/pre_workload.yml new file mode 100644 index 00000000000..62abb85eb3a --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/tasks/pre_workload.yml @@ -0,0 +1,35 @@ +--- +# Pre-workload tasks for namespaced example + +- name: Set up workload for namespace deployment + debug: + msg: "Setting up namespaced workload - no bastion or SSH access required" + +# Set up the combined dictionary for the workload +- name: Set up ocp_workloads_namespaced_example combined dictionary + set_fact: + ocp_workloads_namespaced_example: >- + {{ ocp_workloads_namespaced_example_defaults + | combine(ocp_workloads_namespaced_example_vars | default( {} ), + ocp_workloads_namespaced_example_secrets | default( {} ), recursive=true ) + }} + +# Validate required connection parameters +- name: Validate OpenShift connection parameters + assert: + that: + - sandbox_openshift_api_url | default('') | length > 0 + - sandbox_openshift_namespace | default('') | length > 0 + - (sandbox_openshift_api_token | default('') | length > 0) or (sandbox_openshift_user | default('') | length > 0 and sandbox_openshift_password | default('') | length > 0) + fail_msg: "Required OpenShift connection parameters are missing. Need sandbox_openshift_api_url, sandbox_openshift_namespace, and either sandbox_openshift_api_token or sandbox_openshift_user/sandbox_openshift_password" + +# Print combined role variables (verbosity 2 to hide secrets) +- name: Print combined role variables + debug: + var: ocp_workloads_namespaced_example + verbosity: 2 + +- name: pre_workload tasks complete + debug: + msg: "Pre-Workload tasks completed successfully." + when: not silent | bool diff --git a/ansible/roles/ocp_workloads_namespaced_example/tasks/remove_workload.yml b/ansible/roles/ocp_workloads_namespaced_example/tasks/remove_workload.yml new file mode 100644 index 00000000000..5cbd593d78b --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/tasks/remove_workload.yml @@ -0,0 +1,120 @@ +--- +# Workload removal tasks demonstrating both oc command and kubernetes.core approaches + +# Use oc command for authentication (consistent with deployment) +- name: Login to OpenShift cluster with token for removal + ansible.builtin.shell: | + oc login --server={{ sandbox_openshift_api_url }} \ + --token={{ sandbox_openshift_api_token }} \ + --insecure-skip-tls-verify=true + when: sandbox_openshift_api_token | default('') | length > 0 + no_log: true + register: oc_login_result + retries: 3 + delay: 10 + until: oc_login_result.rc == 0 + +- name: Login to OpenShift cluster with username/password for removal + ansible.builtin.shell: | + oc login --server={{ sandbox_openshift_api_url }} \ + --username={{ sandbox_openshift_user }} \ + --password={{ sandbox_openshift_password }} \ + --insecure-skip-tls-verify=true + when: + - sandbox_openshift_api_token | default('') | length == 0 + - sandbox_openshift_user | default('') | length > 0 + no_log: true + register: oc_login_user_result + retries: 3 + delay: 10 + until: oc_login_user_result.rc == 0 + +- name: Set target namespace for removal + ansible.builtin.shell: oc project {{ sandbox_openshift_namespace }} + register: oc_project_result + retries: 3 + delay: 5 + until: oc_project_result.rc == 0 + +# Use kubernetes.core module for route removal (consistent with deployment approach) +- name: Remove Route using kubernetes.core module + when: ocp_workloads_namespaced_example_defaults.create_route | bool + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: route.openshift.io/v1 + kind: Route + name: "{{ ocp_workloads_namespaced_example_defaults.app_name }}-route" + namespace: "{{ sandbox_openshift_namespace }}" + state: absent + register: route_delete_result + +# Use kubernetes.core module for standard Kubernetes resources +- name: Remove Service using kubernetes.core module + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: v1 + kind: Service + name: "{{ ocp_workloads_namespaced_example_defaults.app_name }}-service" + namespace: "{{ sandbox_openshift_namespace }}" + state: absent + +- name: Remove Deployment using kubernetes.core module + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: apps/v1 + kind: Deployment + name: "{{ ocp_workloads_namespaced_example_defaults.app_name }}" + namespace: "{{ sandbox_openshift_namespace }}" + state: absent + wait: true + wait_timeout: 300 + +# Verify removal using oc command +- name: Verify all resources are removed using oc command + ansible.builtin.shell: | + oc get deployment,service,route -l app={{ ocp_workloads_namespaced_example_defaults.app_name }} \ + -n {{ sandbox_openshift_namespace }} --ignore-not-found=true + register: verify_removal_result + +- name: Display removal summary + debug: + msg: + - "=== Removal Summary ===" + - "Application '{{ ocp_workloads_namespaced_example_defaults.app_name }}' removal completed" + - "Namespace: {{ sandbox_openshift_namespace }}" + - "" + - "=== Methods Demonstrated ===" + - "Authentication: oc login command" + - "Route, Service & Deployment removal: kubernetes.core.k8s module" + - "Verification: oc get command" + - "" + - "Remaining resources: {{ verify_removal_result.stdout | default('None') }}" + when: not silent | bool + +- name: Send removal status to AgnosticD UI + agnosticd_user_info: + msg: "Namespaced workload '{{ ocp_workloads_namespaced_example_defaults.app_name }}' removed successfully." + data: + workload_name: "{{ ocp_workloads_namespaced_example_defaults.app_name }}" + namespace: "{{ sandbox_openshift_namespace }}" + removal_method: "kubernetes.core.k8s module" + remaining_resources: "{{ verify_removal_result.stdout | default('None') }}" + openshift_cluster: "{{ sandbox_openshift_api_url }}" + removal_status: "completed" + +- name: Workload removal complete + debug: + msg: "Workload '{{ ocp_workloads_namespaced_example_defaults.app_name }}' removed successfully using kubernetes.core approach" + when: not silent | bool diff --git a/ansible/roles/ocp_workloads_namespaced_example/tasks/workload.yml b/ansible/roles/ocp_workloads_namespaced_example/tasks/workload.yml new file mode 100644 index 00000000000..1b3b0640b6a --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/tasks/workload.yml @@ -0,0 +1,163 @@ +--- +# Main workload tasks for namespaced example deployment +# This demonstrates both oc command and kubernetes.core module approaches + +# Option 1: Use oc command for authentication (more robust for connectivity issues) +- name: Login to OpenShift cluster with oc command + ansible.builtin.shell: | + oc login --server={{ sandbox_openshift_api_url }} \ + --token={{ sandbox_openshift_api_token }} \ + --insecure-skip-tls-verify=true + when: sandbox_openshift_api_token | default('') | length > 0 + no_log: true + register: oc_login_result + retries: 3 + delay: 10 + until: oc_login_result.rc == 0 + +- name: Login to OpenShift cluster with username/password + ansible.builtin.shell: | + oc login --server={{ sandbox_openshift_api_url }} \ + --username={{ sandbox_openshift_user }} \ + --password={{ sandbox_openshift_password }} \ + --insecure-skip-tls-verify=true + when: + - sandbox_openshift_api_token | default('') | length == 0 + - sandbox_openshift_user | default('') | length > 0 + no_log: true + register: oc_login_user_result + retries: 3 + delay: 10 + until: oc_login_user_result.rc == 0 + +- name: Set target namespace using oc command + ansible.builtin.shell: oc project {{ sandbox_openshift_namespace }} + register: oc_project_result + retries: 3 + delay: 5 + until: oc_project_result.rc == 0 + +# Use kubernetes.core module with template + from_yaml for proper type handling +- name: Deploy example application using kubernetes.core module + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + namespace: "{{ sandbox_openshift_namespace }}" + wait_timeout: 120 + force: true + definition: "{{ lookup('template', 'deployment.yaml.j2') | from_yaml }}" + state: present + register: deployment_result + retries: 3 + delay: 10 + until: deployment_result is succeeded + +- name: Create Service using kubernetes.core module + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + namespace: "{{ sandbox_openshift_namespace }}" + definition: "{{ lookup('template', 'service.yaml.j2') | from_yaml }}" + state: present + register: service_result + retries: 3 + delay: 10 + until: service_result is succeeded + +- name: Create Route using kubernetes.core module + when: ocp_workloads_namespaced_example.create_route | bool + kubernetes.core.k8s: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + namespace: "{{ sandbox_openshift_namespace }}" + definition: "{{ lookup('template', 'route.yaml.j2') | from_yaml }}" + state: present + register: route_result + retries: 3 + delay: 10 + until: route_result is succeeded + +# Option 4: Mix both approaches for verification +- name: Verify deployment is ready using kubernetes.core module + kubernetes.core.k8s_info: + host: "{{ sandbox_openshift_api_url }}" + api_key: "{{ sandbox_openshift_api_token | default(omit) }}" + username: "{{ sandbox_openshift_user | default(omit) }}" + password: "{{ sandbox_openshift_password | default(omit) }}" + validate_certs: false + api_version: apps/v1 + kind: Deployment + name: "{{ ocp_workloads_namespaced_example.app_name }}" + namespace: "{{ sandbox_openshift_namespace }}" + wait: true + wait_condition: + type: Available + status: "True" + wait_timeout: 300 + register: deployment_status + +- name: Get route URL using oc command (EXAMPLE) + when: ocp_workloads_namespaced_example.create_route | bool + ansible.builtin.shell: oc get route {{ ocp_workloads_namespaced_example.app_name }}-route -o jsonpath='{.spec.host}' + register: route_url_result + + +- name: Display deployment information showing both approaches + debug: + msg: + - "=== Deployment Summary ===" + - "Application: {{ ocp_workloads_namespaced_example.app_name }}" + - "Namespace: {{ sandbox_openshift_namespace }}" + - "Image: {{ ocp_workloads_namespaced_example.app_image }}" + - "Replicas: {{ ocp_workloads_namespaced_example.app_replicas }}" + - "" + - "=== Methods Demonstrated ===" + - "Authentication: oc login command" + - "Deployment & Service: kubernetes.core.k8s module" + - "Route: kubernetes.core.k8s module with template" + - "Verification: kubernetes.core.k8s_info module" + - "" + - "=== Access Information ===" + - "Ready replicas: {{ deployment_status.resources[0].status.readyReplicas | default(0) }}/{{ ocp_workloads_namespaced_example.app_replicas }}" + - "{% if ocp_workloads_namespaced_example.create_route and route_url_result is defined and route_url_result.stdout %}Application URL: http://{{ route_url_result.stdout }}{% endif %}" + when: not silent | bool + +- name: Send structured data to AgnosticD UI + agnosticd_user_info: + msg: | + Namespaced workload '{{ ocp_workloads_namespaced_example.app_name }}' deployed successfully. + {% if ocp_workloads_namespaced_example.create_route and route_url_result is defined and route_url_result.stdout %} + Application URL: http://{{ route_url_result.stdout }} + {% endif %} + data: + workload_name: "{{ ocp_workloads_namespaced_example.app_name }}" + namespace: "{{ sandbox_openshift_namespace }}" + image: "{{ ocp_workloads_namespaced_example.app_image }}" + replicas: "{{ ocp_workloads_namespaced_example.app_replicas }}" + ready_replicas: "{{ deployment_status.resources[0].status.readyReplicas | default(0) }}" + service_port: "{{ ocp_workloads_namespaced_example.service_port }}" + deployment_method: "kubernetes.core with templates" + openshift_cluster: "{{ sandbox_openshift_api_url }}" + deployment_status: "ready" + +- name: Save route info to data + when: ocp_workloads_namespaced_example.create_route | bool and route_url_result is defined and route_url_result.stdout + agnosticd_user_info: + data: + application_url: "https://{{ route_url_result.stdout }}" + route_host: "{{ route_url_result.stdout }}" + +# Leave this as the last task in the playbook. +- name: workload tasks complete + debug: + msg: "Workload Tasks completed successfully using mixed oc/kubernetes.core approach." + when: not silent | bool diff --git a/ansible/roles/ocp_workloads_namespaced_example/templates/deployment.yaml.j2 b/ansible/roles/ocp_workloads_namespaced_example/templates/deployment.yaml.j2 new file mode 100644 index 00000000000..397250666a3 --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/templates/deployment.yaml.j2 @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ ocp_workloads_namespaced_example.app_name }} + namespace: {{ sandbox_openshift_namespace }} + labels: + app: {{ ocp_workloads_namespaced_example.app_name }} + deployment-method: kubernetes-module +spec: + replicas: {{ ocp_workloads_namespaced_example.app_replicas | default(1) | int }} + selector: + matchLabels: + app: {{ ocp_workloads_namespaced_example.app_name }} + template: + metadata: + labels: + app: {{ ocp_workloads_namespaced_example.app_name }} + spec: + containers: + - name: {{ ocp_workloads_namespaced_example.app_name }} + image: {{ ocp_workloads_namespaced_example.app_image }} + ports: + - containerPort: {{ ocp_workloads_namespaced_example.service_port | int }} + resources: + limits: + cpu: {{ ocp_workloads_namespaced_example.resource_limits.cpu }} + memory: {{ ocp_workloads_namespaced_example.resource_limits.memory }} + requests: + cpu: {{ ocp_workloads_namespaced_example.resource_requests.cpu }} + memory: {{ ocp_workloads_namespaced_example.resource_requests.memory }} \ No newline at end of file diff --git a/ansible/roles/ocp_workloads_namespaced_example/templates/route.yaml.j2 b/ansible/roles/ocp_workloads_namespaced_example/templates/route.yaml.j2 new file mode 100644 index 00000000000..e52f7cda0fe --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/templates/route.yaml.j2 @@ -0,0 +1,15 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ ocp_workloads_namespaced_example.app_name }}-route + namespace: {{ sandbox_openshift_namespace }} + labels: + app: {{ ocp_workloads_namespaced_example.app_name }} + deployment-method: kubernetes-module +spec: + path: {{ ocp_workloads_namespaced_example.route_path }} + to: + kind: Service + name: {{ ocp_workloads_namespaced_example.app_name }}-service + port: + targetPort: {{ ocp_workloads_namespaced_example.service_port | int }} \ No newline at end of file diff --git a/ansible/roles/ocp_workloads_namespaced_example/templates/service.yaml.j2 b/ansible/roles/ocp_workloads_namespaced_example/templates/service.yaml.j2 new file mode 100644 index 00000000000..c4541766a6f --- /dev/null +++ b/ansible/roles/ocp_workloads_namespaced_example/templates/service.yaml.j2 @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ ocp_workloads_namespaced_example.app_name }}-service + namespace: {{ sandbox_openshift_namespace }} + labels: + app: {{ ocp_workloads_namespaced_example.app_name }} + deployment-method: kubernetes-module +spec: + selector: + app: {{ ocp_workloads_namespaced_example.app_name }} + ports: + - port: {{ ocp_workloads_namespaced_example.service_port | int }} + targetPort: {{ ocp_workloads_namespaced_example.service_port | int }} + type: ClusterIP \ No newline at end of file