Repository: PrefectHQ/prefect

Issue:
RBAC option for k8s agent
## Use Case
*Please provide a use case to help us understand your request in context*
I need to deploy an agent to a GKE cluster with RBAC on without problem



## Solution
*Please describe your ideal solution*
add --rbac flag to prefect agent deploy, which adds the necessary RBAC permissions to operate within its given namespace.  this would require also forcing the agent into the same namespace as its children by -n



## Alternatives
*Please describe any alternatives you've considered, even if you've dismissed them*
considered giving a clusterrolebinding instead of a rolebinding so that we don't have to limit the agent to being in the same namespace as its children, but either way, your rolebinding isn't going to be right if u add a namespace after the pipe to kubectl.  u will need to tell your rolebinding which namespace the service account will be in


Discussion:

Resolving patch:
diff --git a/src/prefect/agent/kubernetes/agent.py b/src/prefect/agent/kubernetes/agent.py
--- a/src/prefect/agent/kubernetes/agent.py
+++ b/src/prefect/agent/kubernetes/agent.py
@@ -150,6 +150,7 @@ def generate_deployment_yaml(
         namespace: str = None,
         image_pull_secrets: str = None,
         resource_manager_enabled: bool = False,
+        rbac: bool = False,
         labels: Iterable[str] = None,
     ) -> str:
         """
@@ -165,6 +166,8 @@ def generate_deployment_yaml(
                 for Prefect jobs
             - resource_manager_enabled (bool, optional): Whether to include the resource
                 manager as part of the YAML. Defaults to `False`
+            - rbac (bool, optional): Whether to include default RBAC configuration as
+                part of the YAML. Defaults to `False`
             - labels (List[str], optional): a list of labels, which are arbitrary string
                 identifiers used by Prefect Agents when polling for work
 
@@ -223,7 +226,19 @@ def generate_deployment_yaml(
         else:
             del deployment["spec"]["template"]["spec"]["imagePullSecrets"]
 
-        return yaml.safe_dump(deployment)
+        # Load RBAC if specified
+        rbac_yaml = []
+        if rbac:
+            with open(path.join(path.dirname(__file__), "rbac.yaml"), "r") as rbac_file:
+                rbac_generator = yaml.safe_load_all(rbac_file)
+
+                for document in rbac_generator:
+                    document["metadata"]["namespace"] = namespace
+                    rbac_yaml.append(document)
+
+        output_yaml = [deployment]
+        output_yaml.extend(rbac_yaml)
+        return yaml.safe_dump_all(output_yaml, explicit_start=True)
 
     def heartbeat(self) -> None:
         """
diff --git a/src/prefect/cli/agent.py b/src/prefect/cli/agent.py
--- a/src/prefect/cli/agent.py
+++ b/src/prefect/cli/agent.py
@@ -198,6 +198,7 @@ def start(
 @click.option(
     "--resource-manager", is_flag=True, help="Enable resource manager.", hidden=True
 )
+@click.option("--rbac", is_flag=True, help="Enable default RBAC.", hidden=True)
 @click.option(
     "--label",
     "-l",
@@ -226,6 +227,7 @@ def install(
     namespace,
     image_pull_secrets,
     resource_manager,
+    rbac,
     label,
     import_path,
     show_flow_logs,
@@ -250,6 +252,7 @@ def install(
         --namespace, -n             TEXT    Agent namespace to launch workloads
         --image-pull-secrets, -i    TEXT    Name of image pull secrets to use for workloads
         --resource-manager                  Enable resource manager on install
+        --rbac                              Enable default RBAC on install
 
     \b
     Local Agent Options:
@@ -276,6 +279,7 @@ def install(
             namespace=namespace,
             image_pull_secrets=image_pull_secrets,
             resource_manager_enabled=resource_manager,
+            rbac=rbac,
             labels=list(label),
         )
         click.echo(deployment)


Test patch:
diff --git a/tests/agent/test_k8s_agent.py b/tests/agent/test_k8s_agent.py
--- a/tests/agent/test_k8s_agent.py
+++ b/tests/agent/test_k8s_agent.py
@@ -343,6 +343,24 @@ def test_k8s_agent_generate_deployment_yaml_contains_image_pull_secrets(
     )
 
 
+def test_k8s_agent_generate_deployment_yaml_rbac(monkeypatch, runner_token):
+    k8s_config = MagicMock()
+    monkeypatch.setattr("kubernetes.config", k8s_config)
+
+    agent = KubernetesAgent()
+    deployment = agent.generate_deployment_yaml(
+        token="test_token", api="test_api", namespace="test_namespace", rbac=True
+    )
+
+    deployment = yaml.safe_load_all(deployment)
+
+    for document in deployment:
+        if "rbac" in document:
+            assert "rbac" in document["apiVersion"]
+            assert document["metadata"]["namespace"] == "test_namespace"
+            assert document["metadata"]["name"] == "prefect-agent-rbac"
+
+
 def test_k8s_agent_heartbeat_creates_file(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
diff --git a/tests/cli/test_agent.py b/tests/cli/test_agent.py
--- a/tests/cli/test_agent.py
+++ b/tests/cli/test_agent.py
@@ -248,6 +248,7 @@ def test_agent_install_k8s_asses_args():
             "--namespace",
             "TEST_NAMESPACE",
             "--resource-manager",
+            "--rbac",
             "--image-pull-secrets",
             "secret-test",
             "--label",
@@ -261,6 +262,7 @@ def test_agent_install_k8s_asses_args():
     assert "TEST_API" in result.output
     assert "TEST_NAMESPACE" in result.output
     assert "resource-manager" in result.output
+    assert "rbac" in result.output
     assert "secret-test" in result.output
     assert "test_label1" in result.output
     assert "test_label2" in result.output
@@ -281,6 +283,7 @@ def test_agent_install_k8s_no_resource_manager():
             "TEST_NAMESPACE",
             "--image-pull-secrets",
             "secret-test",
+            "--rbac",
         ],
     )
     assert result.exit_code == 0
@@ -288,6 +291,7 @@ def test_agent_install_k8s_no_resource_manager():
     assert "TEST_API" in result.output
     assert "TEST_NAMESPACE" in result.output
     assert not "resource-manager" in result.output
+    assert "rbac" in result.output
     assert "secret-test" in result.output