Skip to content

feat: Kafka agent - #1028

Draft
labrenbe wants to merge 17 commits into
mainfrom
feat/kafka-agent
Draft

labrenbe wants to merge 17 commits into
mainfrom
feat/kafka-agent

Conversation

@labrenbe

@labrenbe labrenbe commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Description

Part of stackabletech/issues#888.

Depends on decisions

Depends on PRs

Deploys an agent alongside Kafka clusters which is an independent controller that reconciles and manages internal resources like KafkaTopic.

This PR only adds an empty agent shell that doesn't do anything yet.

Definition of Done Checklist

  • Not all of these items are applicable to all PRs, the author should update this template to only leave the boxes in that are relevant
  • Please make sure all these things are done and tick the boxes

Author

  • Changes are OpenShift compatible
  • CRD changes approved
  • CRD documentation for all fields, following the style guide.
  • Helm chart can be installed and deployed operator works
  • Integration tests passed (for non trivial changes)
  • Changes need to be "offline" compatible
  • Links to generated (nightly) docs added
  • Release note snippet added

Reviewer

  • Code contains useful comments
  • Code contains useful logging statements
  • (Integration-)Test cases added
  • Documentation added or updated. Follows the style guide.
  • Changelog updated
  • Cargo.toml only contains references to git tags (not specific commits or branches)

Acceptance

  • Feature Tracker has been updated
  • Proper release label has been added
  • Links to generated (nightly) docs added
  • Release note snippet added
  • Add type/deprecation label & add to the deprecation schedule
  • Add type/experimental label & add to the experimental features tracker

@labrenbe labrenbe changed the title feat: Kafka agent shell feat: Kafka agent Sep 25, 2026
@labrenbe labrenbe self-assigned this Sep 25, 2026
@labrenbe labrenbe moved this to Development: In Progress in Stackable Engineering Sep 25, 2026
Comment on lines +70 to +77
.resources(
ResourceRequirementsBuilder::new()
.with_cpu_request("100m")
.with_cpu_limit("500m")
.with_memory_request("128Mi")
.with_memory_limit("128Mi")
.build(),
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not hardcode the resources but offer them (in the CRD, not just overrides) and use defaults?

Comment thread Cargo.toml

[workspace.dependencies]
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.118.0", features = ["crds", "webhook"] }
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", branch = "feat/platform-access", features = ["crds", "webhook"] }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: revert to a tag when ready.

Comment thread rust/operator-binary/src/crd/mod.rs Outdated
pub struct KafkaPlatformAccess {
/// Whether the operator deploys the agent. False by default.
#[serde(default)]
pub enabled: bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this field if the whole struct is an Option? Is it just so that KafkaPlatformAccessAuthentication can be left in the CR (and turned on or off)? If so, that is a different pattern used by e.g. clusterConfig.authentication (which is Vec and which is either present or not) and we would need a good reason to use it.

.with_labels(recommended_labels_for_agent_resources(cluster))
.build(),
)
.image_pull_secrets_from_product_image(&cluster.image)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that the agent image can use the same credentials as the product image is fine as long as they come from the same registry. If they don't, there should be an optional field platformAccess.image.pullSecrets that allows the user to specify these independently; when unset, the agent falls back to spec.image.pullSecrets as it currently does.

Comment thread rust/operator-binary/src/main.rs Outdated
)
.owns(
watch_namespace.get_api::<DeserializeGuard<Deployment>>(&client),
// The agent is a the only Deployment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo or incomplete comment? Something like: "The agent Deployment is the only one the operator owns"?

.image(&agent_config.image)
.command(vec!["stackable-kafka-agent".to_owned()])
.args(vec!["run".to_owned()])
.add_env_var_from_source(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reverts to the pre-smooth-op changes: the pattern that should now be used is to have the env-var names defined as constants:

constant!(KUBERNETES_NODE_NAME: EnvVarName = "KUBERNETES_NODE_NAME");
constant!(KUBERNETES_CLUSTER_DOMAIN: EnvVarName = "KUBERNETES_CLUSTER_DOMAIN");

and to use these in e.g. a helper function that returns an EnvVarSet (the .expect is safe because the name is a compile-time constant) e.g.

fn agent_env_vars(cluster: &ValidatedCluster) -> EnvVarSet {
    let node_name_env_var = EnvVar {
        name: KUBERNETES_NODE_NAME.to_string(),
        value: None,
        value_from: Some(EnvVarSource {
            field_ref: Some(ObjectFieldSelector {
                field_path: "spec.nodeName".to_owned(),
                ..ObjectFieldSelector::default()
            }),
            ..EnvVarSource::default()
        }),
    };

    EnvVarSet::new()
        // Saves the agent from looking the domain up via the kubelet, which needs extra RBAC.
        .with_value(
            &KUBERNETES_CLUSTER_DOMAIN,
            cluster.cluster_domain.to_string(),
        )
        .with_env_var(node_name_env_var)
        .expect("the env var name is a valid EnvVarName")
}

and then add this to the ContainerBuilder inline e.g.

    cb_agent
        ...
        .add_env_vars(agent_env_vars(cluster))

see e.g. https://github.com/stackabletech/kafka-operator/blob/feat/kafka-agent/rust/operator-binary/src/controller/build/resource/statefulset.rs#L136

}
}

pub fn build_agent_service_account(cluster: &ValidatedCluster) -> ServiceAccount {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rbac.rs delegates to op-rs https://github.com/stackabletech/kafka-operator/blob/feat/kafka-agent/rust/operator-binary/src/controller/build/resource/rbac.rs#L11-L27 but here they are built manually. If we add an agent-aware AgentResourceNames to op-rs, and let the v2::rbac builders take either name type (e.g. via a small trait over service_account_name / role_binding_name / cluster_role_name), we could do the same thing here. Then we could do this:

controller.rs:

pub fn agent_resource_names(&self) -> AgentResourceNames {
    AgentResourceNames {
        cluster_name: self.name.clone(),
        product_name: PRODUCT_NAME.clone(),
    }
}

agent.rs:

pub fn build_agent_service_account(cluster: &ValidatedCluster) -> ServiceAccount {
    rbac::build_service_account(
        cluster,
        &cluster.agent_resource_names(),
        recommended_labels_for_agent_resources(cluster),
    )
}

pub fn build_agent_role_binding(cluster: &ValidatedCluster) -> RoleBinding {
    rbac::build_role_binding(
        cluster,
        &cluster.agent_resource_names(),
        recommended_labels_for_agent_resources(cluster),
    )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants