|
83 | 83 | )
|
84 | 84 |
|
85 | 85 | import codeflare_sdk.utils.kube_api_helpers
|
86 |
| -from codeflare_sdk.utils.generate_yaml import gen_names, is_openshift_cluster |
| 86 | +from codeflare_sdk.utils.generate_yaml import ( |
| 87 | + gen_names, |
| 88 | + is_openshift_cluster, |
| 89 | + read_template, |
| 90 | + enable_local_interactive, |
| 91 | +) |
87 | 92 |
|
88 | 93 | import openshift
|
89 | 94 | from openshift.selector import Selector
|
@@ -2563,6 +2568,171 @@ def test_export_env():
|
2563 | 2568 | )
|
2564 | 2569 |
|
2565 | 2570 |
|
| 2571 | +def test_enable_local_interactive(mocker): |
| 2572 | + template = f"{parent}/src/codeflare_sdk/templates/base-template.yaml" |
| 2573 | + user_yaml = read_template(template) |
| 2574 | + aw_spec = user_yaml.get("spec", None) |
| 2575 | + cluster_name = "test-enable-local" |
| 2576 | + namespace = "default" |
| 2577 | + ingress_domain = "mytest.domain" |
| 2578 | + mocker.patch( |
| 2579 | + "codeflare_sdk.utils.generate_yaml.is_openshift_cluster", return_value=False |
| 2580 | + ) |
| 2581 | + volume_mounts = [ |
| 2582 | + {"name": "ca-vol", "mountPath": "/home/ray/workspace/ca", "readOnly": True}, |
| 2583 | + { |
| 2584 | + "name": "server-cert", |
| 2585 | + "mountPath": "/home/ray/workspace/tls", |
| 2586 | + "readOnly": False, |
| 2587 | + }, |
| 2588 | + ] |
| 2589 | + volumes = [ |
| 2590 | + { |
| 2591 | + "name": "ca-vol", |
| 2592 | + "secret": {"secretName": f"ca-secret-{cluster_name}"}, |
| 2593 | + "optional": False, |
| 2594 | + }, |
| 2595 | + {"name": "server-cert", "emptyDir": {}}, |
| 2596 | + ] |
| 2597 | + tls_env = [ |
| 2598 | + {"name": "RAY_USE_TLS", "value": "1"}, |
| 2599 | + {"name": "RAY_TLS_SERVER_CERT", "value": "/home/ray/workspace/tls/server.crt"}, |
| 2600 | + {"name": "RAY_TLS_SERVER_KEY", "value": "/home/ray/workspace/tls/server.key"}, |
| 2601 | + {"name": "RAY_TLS_CA_CERT", "value": "/home/ray/workspace/tls/ca.crt"}, |
| 2602 | + ] |
| 2603 | + assert aw_spec != None |
| 2604 | + enable_local_interactive(aw_spec, cluster_name, namespace, ingress_domain) |
| 2605 | + head_group_spec = aw_spec["resources"]["GenericItems"][0]["generictemplate"][ |
| 2606 | + "spec" |
| 2607 | + ]["headGroupSpec"] |
| 2608 | + worker_group_spec = aw_spec["resources"]["GenericItems"][0]["generictemplate"][ |
| 2609 | + "spec" |
| 2610 | + ]["workerGroupSpecs"] |
| 2611 | + ca_secret = aw_spec["resources"]["GenericItems"][3]["generictemplate"] |
| 2612 | + # At a minimal, make sure the following items are presented in the appwrapper spec.resources. |
| 2613 | + # 1. headgroup has the initContainers command to generated TLS cert from the mounted CA cert. |
| 2614 | + # Note: In this particular command, the DNS.5 in [alt_name] must match the exposed local_client_url: rayclient-{cluster_name}.{namespace}.{ingress_domain} |
| 2615 | + assert ( |
| 2616 | + head_group_spec["template"]["spec"]["initContainers"][0]["command"][2] |
| 2617 | + == f"cd /home/ray/workspace/tls && openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj '/CN=ray-head' && printf \"authorityKeyIdentifier=keyid,issuer\\nbasicConstraints=CA:FALSE\\nsubjectAltName = @alt_names\\n[alt_names]\\nDNS.1 = 127.0.0.1\\nDNS.2 = localhost\\nDNS.3 = ${{FQ_RAY_IP}}\\nDNS.4 = $(awk 'END{{print $1}}' /etc/hosts)\\nDNS.5 = rayclient-{cluster_name}-$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).{ingress_domain}\">./domain.ext && cp /home/ray/workspace/ca/* . && openssl x509 -req -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -days 365 -CAcreateserial -extfile domain.ext" |
| 2618 | + ) |
| 2619 | + assert ( |
| 2620 | + head_group_spec["template"]["spec"]["initContainers"][0]["volumeMounts"] |
| 2621 | + == volume_mounts |
| 2622 | + ) |
| 2623 | + assert head_group_spec["template"]["spec"]["volumes"] == volumes |
| 2624 | + |
| 2625 | + # 2. workerGrooupSpec has the initContainers command to generated TLS cert from the mounted CA cert. |
| 2626 | + assert ( |
| 2627 | + worker_group_spec[0]["template"]["spec"]["initContainers"][1]["command"][2] |
| 2628 | + == "cd /home/ray/workspace/tls && openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj '/CN=ray-head' && printf \"authorityKeyIdentifier=keyid,issuer\\nbasicConstraints=CA:FALSE\\nsubjectAltName = @alt_names\\n[alt_names]\\nDNS.1 = 127.0.0.1\\nDNS.2 = localhost\\nDNS.3 = ${FQ_RAY_IP}\\nDNS.4 = $(awk 'END{print $1}' /etc/hosts)\">./domain.ext && cp /home/ray/workspace/ca/* . && openssl x509 -req -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -days 365 -CAcreateserial -extfile domain.ext" |
| 2629 | + ) |
| 2630 | + assert ( |
| 2631 | + worker_group_spec[0]["template"]["spec"]["initContainers"][1]["volumeMounts"] |
| 2632 | + == volume_mounts |
| 2633 | + ) |
| 2634 | + assert worker_group_spec[0]["template"]["spec"]["volumes"] == volumes |
| 2635 | + |
| 2636 | + # 3. Required Envs to enable TLS encryption between head and workers |
| 2637 | + for i in range(len(tls_env)): |
| 2638 | + assert ( |
| 2639 | + head_group_spec["template"]["spec"]["containers"][0]["env"][i + 1]["name"] |
| 2640 | + == tls_env[i]["name"] |
| 2641 | + ) |
| 2642 | + assert ( |
| 2643 | + head_group_spec["template"]["spec"]["containers"][0]["env"][i + 1]["value"] |
| 2644 | + == tls_env[i]["value"] |
| 2645 | + ) |
| 2646 | + assert ( |
| 2647 | + worker_group_spec[0]["template"]["spec"]["containers"][0]["env"][i + 1][ |
| 2648 | + "name" |
| 2649 | + ] |
| 2650 | + == tls_env[i]["name"] |
| 2651 | + ) |
| 2652 | + assert ( |
| 2653 | + worker_group_spec[0]["template"]["spec"]["containers"][0]["env"][i + 1][ |
| 2654 | + "value" |
| 2655 | + ] |
| 2656 | + == tls_env[i]["value"] |
| 2657 | + ) |
| 2658 | + |
| 2659 | + # 4. Secret with ca.crt and ca.key |
| 2660 | + assert ca_secret["kind"] == "Secret" |
| 2661 | + assert ca_secret["data"]["ca.crt"] != None |
| 2662 | + assert ca_secret["data"]["ca.key"] != None |
| 2663 | + assert ca_secret["metadata"]["name"] == f"ca-secret-{cluster_name}" |
| 2664 | + assert ca_secret["metadata"]["namespace"] == namespace |
| 2665 | + |
| 2666 | + # 5. Rayclient ingress - Kind |
| 2667 | + rayclient_ingress = aw_spec["resources"]["GenericItems"][2]["generictemplate"] |
| 2668 | + paths = [ |
| 2669 | + { |
| 2670 | + "backend": { |
| 2671 | + "service": { |
| 2672 | + "name": f"{cluster_name}-head-svc", |
| 2673 | + "port": {"number": 10001}, |
| 2674 | + } |
| 2675 | + }, |
| 2676 | + "path": "", |
| 2677 | + "pathType": "ImplementationSpecific", |
| 2678 | + } |
| 2679 | + ] |
| 2680 | + |
| 2681 | + assert rayclient_ingress["kind"] == "Ingress" |
| 2682 | + assert rayclient_ingress["metadata"]["namespace"] == namespace |
| 2683 | + assert rayclient_ingress["metadata"]["annotations"] == { |
| 2684 | + "nginx.ingress.kubernetes.io/rewrite-target": "/", |
| 2685 | + "nginx.ingress.kubernetes.io/ssl-redirect": "true", |
| 2686 | + "nginx.ingress.kubernetes.io/ssl-passthrough": "true", |
| 2687 | + } |
| 2688 | + assert rayclient_ingress["metadata"]["name"] == f"rayclient-{cluster_name}" |
| 2689 | + assert rayclient_ingress["spec"]["rules"][0] == { |
| 2690 | + "host": f"rayclient-{cluster_name}-{namespace}.{ingress_domain}", |
| 2691 | + "http": {"paths": paths}, |
| 2692 | + } |
| 2693 | + # 5.1 Rayclient ingress - OCP |
| 2694 | + user_yaml = read_template(template) |
| 2695 | + aw_spec = user_yaml.get("spec", None) |
| 2696 | + cluster_name = "test-ocp-enable-local" |
| 2697 | + namespace = "default" |
| 2698 | + ocp_cluster_domain = {"spec": {"domain": "mytest.ocp.domain"}} |
| 2699 | + ingress_domain = ocp_cluster_domain["spec"]["domain"] |
| 2700 | + mocker.patch( |
| 2701 | + "codeflare_sdk.utils.generate_yaml.is_openshift_cluster", return_value=True |
| 2702 | + ) |
| 2703 | + mocker.patch( |
| 2704 | + "kubernetes.client.CustomObjectsApi.get_cluster_custom_object", |
| 2705 | + return_value=ocp_cluster_domain, |
| 2706 | + ) |
| 2707 | + paths = [ |
| 2708 | + { |
| 2709 | + "backend": { |
| 2710 | + "service": { |
| 2711 | + "name": f"{cluster_name}-head-svc", |
| 2712 | + "port": {"number": 10001}, |
| 2713 | + } |
| 2714 | + }, |
| 2715 | + "path": "", |
| 2716 | + "pathType": "ImplementationSpecific", |
| 2717 | + } |
| 2718 | + ] |
| 2719 | + enable_local_interactive(aw_spec, cluster_name, namespace, ingress_domain) |
| 2720 | + rayclient_ocp_ingress = aw_spec["resources"]["GenericItems"][2]["generictemplate"] |
| 2721 | + assert rayclient_ocp_ingress["kind"] == "Ingress" |
| 2722 | + assert rayclient_ocp_ingress["metadata"]["annotations"] == { |
| 2723 | + "nginx.ingress.kubernetes.io/rewrite-target": "/", |
| 2724 | + "nginx.ingress.kubernetes.io/ssl-redirect": "true", |
| 2725 | + "route.openshift.io/termination": "passthrough", |
| 2726 | + } |
| 2727 | + assert rayclient_ocp_ingress["metadata"]["name"] == f"rayclient-{cluster_name}" |
| 2728 | + assert rayclient_ocp_ingress["metadata"]["namespace"] == namespace |
| 2729 | + assert rayclient_ocp_ingress["spec"]["ingressClassName"] == "openshift-default" |
| 2730 | + assert rayclient_ocp_ingress["spec"]["rules"][0] == { |
| 2731 | + "host": f"rayclient-{cluster_name}-{namespace}.{ingress_domain}", |
| 2732 | + "http": {"paths": paths}, |
| 2733 | + } |
| 2734 | + |
| 2735 | + |
2566 | 2736 | def test_create_openshift_oauth(mocker: MockerFixture):
|
2567 | 2737 | create_namespaced_service_account = MagicMock()
|
2568 | 2738 | create_cluster_role_binding = MagicMock()
|
|
0 commit comments