Elev8 ☁️ - Cloud-Native Kubernetes Java Client
A lightweight, cloud-native Kubernetes Java client that eliminates configuration complexity by providing native support for AWS IAM authentication (EKS), GCP authentication (GKE), and Azure authentication (AKS) - without requiring kubectl or complex kubeconfig management.
Table of Contents
- Why Elev8?
- Features
- Prerequisites
- Quick Start
- Maven Dependency
- Basic Usage
- IAM Authentication Examples
- OIDC/IRSA Authentication
- ConfigMaps
- Secrets
- DaemonSets
- Jobs
- StatefulSets
- ReplicaSets
- Ingress
- CronJobs
- Namespaces
- ServiceAccounts
- Roles
- RoleBindings
- ClusterRoles
- ClusterRoleBindings
- NetworkPolicies
- HorizontalPodAutoscalers
- VerticalPodAutoscalers
- ResourceQuotas
- PersistentVolumes
- PersistentVolumeClaims
- EKS Access Entries
- Watch API
- Pod Log Streaming
- Patch Operations
- Server-side Apply
- Exec into Pods
- Port Forwarding
- Authentication Modes Comparison
- Project Structure
- Building from Source
- Logging
- Examples
- Migration from fabric8
- kubectl Command Equivalents
- Pod Operations
- Service Operations
- Deployment Operations
- ConfigMap Operations
- Secret Operations
- DaemonSet Operations
- Job Operations
- StatefulSet Operations
- ReplicaSet Operations
- Ingress Operations
- CronJob Operations
- Namespace Operations
- ServiceAccount Operations
- Role Operations
- RoleBinding Operations
- ClusterRole Operations
- ClusterRoleBinding Operations
- NetworkPolicy Operations
- HorizontalPodAutoscaler Operations
- VerticalPodAutoscaler Operations
- ResourceQuota Operations
- PersistentVolume Operations
- PersistentVolumeClaim Operations
- Contributing
- License
- Support
- Roadmap
Why Elev8?
Existing Kubernetes Java clients (fabric8, official client) struggle with cloud provider authentication:
- Complex IAM Authentication: Difficult to configure cloud IAM roles and temporary credentials
- Manual Token Management: No automatic token refresh or credential management
- Heavy Dependencies: Large dependency trees causing version conflicts
- Missing Cloud Features: No support for modern cloud features like EKS Access Entries API
Elev8 solves these problems with:
- Zero kubeconfig configuration needed
- Native cloud provider API support (EKS Access Entries, GKE, AKS)
- Automatic IAM token generation and refresh
- Minimal dependencies
- Cloud-first design with multi-cloud support (EKS/GKE/AKS)
- Clear documentation for cloud provider scenarios
Features
Authentication Modes
- IAM Roles: Automatic STS token generation with AWS Signature V4
- OIDC/IRSA: Service account web identity token exchange for pods
- EKS Access Entries: Native integration with EKS Access Entries API (all 3 auth modes)
- Service Account Tokens: Traditional Kubernetes token-based auth
Core Capabilities
- Fluent API: Clean, type-safe builder pattern for client configuration
- Auto-Discovery: Automatic cluster endpoint and CA certificate retrieval
- Extensible: Plugin architecture for custom resources and extensions
- Multi-Cluster: Simple configuration for multiple EKS clusters
- Minimal Dependencies: Only AWS SDK 2.x, Jackson, SLF4J, and OkHttp
Prerequisites
System Requirements
- Operating System: macOS, Linux, or Windows
- Java: 17 or later
- Maven: 3.8 or later
- AWS Account: With EKS cluster access
- AWS Credentials: Configured via AWS CLI or environment variables
Installation
macOS (via Homebrew)
Install Java 17:
brew install openjdk@17 # Add to your ~/.zshrc or ~/.bash_profile export JAVA_HOME="/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home" export PATH="$JAVA_HOME/bin:$PATH" # Reload shell configuration source ~/.zshrc
Install Maven:
brew install maven
# Verify installation
mvn -version
java -versionLinux (Ubuntu/Debian)
# Install Java 17 sudo apt update sudo apt install openjdk-17-jdk # Install Maven sudo apt install maven # Verify installation mvn -version java -version
Windows
- Download and install OpenJDK 17
- Download and install Maven
- Add JAVA_HOME and Maven to your PATH environment variables
AWS Setup
Configure AWS Credentials:
# Option 1: AWS CLI aws configure # Option 2: Environment Variables export AWS_ACCESS_KEY_ID=your_access_key export AWS_SECRET_ACCESS_KEY=your_secret_key export AWS_REGION=us-east-1
Verify EKS Access:
aws eks list-clusters --region us-east-1 aws eks describe-cluster --name your-cluster --region us-east-1
Quick Start
Maven Dependency
<dependency> <groupId>io.elev8</groupId> <artifactId>elev8-eks</artifactId> <version>0.1.0</version> </dependency>
Basic Usage
import io.elev8.eks.EksClient; import io.elev8.resources.pod.Pod; import java.util.List; // Create client with automatic IAM authentication final EksClient client = EksClient.builder() .clusterName("my-cluster") .region("us-east-1") .build(); // List pods in a namespace final List<Pod> pods = client.pods().list("default"); // Get a specific pod final Pod pod = client.pods().get("default", "my-pod"); // Create a pod final Pod newPod = Pod.builder() .name("nginx") .namespace("default") .spec(PodSpec.builder() .addContainer(Container.builder() .name("nginx") .image("nginx:latest") .build()) .build()) .build(); client.pods().create(newPod);
IAM Authentication Examples
From EC2 Instance
// Uses instance profile credentials automatically final EksClient client = EksClient.builder() .clusterName("my-cluster") .region("us-east-1") .build();
From Lambda Function
// Uses Lambda execution role automatically final EksClient client = EksClient.builder() .clusterName(System.getenv("EKS_CLUSTER_NAME")) .region(System.getenv("AWS_REGION")) .build();
With Assume Role
final EksClient client = EksClient.builder() .clusterName("my-cluster") .region("us-east-1") .roleArn("arn:aws:iam::123456789012:role/EksAdmin") .sessionName("my-session") .build();
With Specific Credentials
final AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create( AwsBasicCredentials.create(accessKey, secretKey)); final EksClient client = EksClient.builder() .clusterName("my-cluster") .region("us-east-1") .baseCredentialsProvider(credentialsProvider) .build();
OIDC/IRSA Authentication
For pods running in EKS with IAM Roles for Service Accounts:
final EksClient client = EksClient.builder() .region("us-east-1") .cluster("my-cluster") .oidcAuth() // Auto-discovers web identity token .build();
ConfigMaps
import io.elev8.resources.configmap.ConfigMap; // Create a ConfigMap with data final ConfigMap configMap = ConfigMap.builder() .name("app-config") .namespace("default") .addData("database.url", "jdbc:postgresql://localhost:5432/mydb") .addData("database.user", "admin") .addData("app.environment", "production") .build(); client.configMaps().create(configMap); // Get a ConfigMap final ConfigMap retrieved = client.configMaps().get("default", "app-config"); // List ConfigMaps in namespace final List<ConfigMap> configMaps = client.configMaps().list("default"); // Create immutable ConfigMap final ConfigMap immutableConfig = ConfigMap.builder() .name("constants") .namespace("default") .addData("version", "1.0.0") .immutable(true) .build();
Secrets
import io.elev8.resources.secret.Secret; // Create a basic authentication secret final Secret basicAuth = Secret.builder() .name("database-credentials") .namespace("default") .basicAuth("admin", "password123") .build(); client.secrets().create(basicAuth); // Create a TLS secret final Secret tlsSecret = Secret.builder() .name("tls-cert") .namespace("default") .tls("base64-encoded-cert", "base64-encoded-key") .build(); // Create an opaque secret with custom data final Secret apiSecret = Secret.builder() .name("api-keys") .namespace("default") .addStringData("api-key", "my-secret-key") .addStringData("api-secret", "my-secret-value") .build(); // Create Docker registry secret final Secret dockerSecret = Secret.builder() .name("docker-registry") .namespace("default") .dockerConfigJson("base64-encoded-docker-config") .build(); // Get a Secret final Secret retrieved = client.secrets().get("default", "api-keys"); // List Secrets in namespace final List<Secret> secrets = client.secrets().list("default");
DaemonSets
import io.elev8.resources.daemonset.DaemonSet; import io.elev8.resources.daemonset.DaemonSetSpec; // Create a DaemonSet for log collection on all nodes final DaemonSet logCollector = DaemonSet.builder() .name("fluentd-elasticsearch") .namespace("kube-system") .spec(DaemonSetSpec.builder() .selector("app", "fluentd") .template(DaemonSetPodTemplateSpec.builder() .label("app", "fluentd") .spec(PodSpec.builder() .container(Container.builder() .name("fluentd") .image("fluent/fluentd-kubernetes-daemonset:v1.14-debian-elasticsearch7") .build()) .build()) .build()) .build()) .build(); client.daemonSets().create(logCollector); // Create a DaemonSet with custom update strategy final DaemonSet nodeExporter = DaemonSet.builder() .name("node-exporter") .namespace("monitoring") .spec(DaemonSetSpec.builder() .selector("app", "node-exporter") .updateStrategy("OnDelete") .minReadySeconds(30) .template(DaemonSetPodTemplateSpec.builder() .label("app", "node-exporter") .spec(PodSpec.builder() .container(Container.builder() .name("node-exporter") .image("prom/node-exporter:latest") .build()) .build()) .build()) .build()) .build(); client.daemonSets().create(nodeExporter); // Get a DaemonSet final DaemonSet retrieved = client.daemonSets().get("kube-system", "fluentd-elasticsearch"); // List DaemonSets in namespace final List<DaemonSet> daemonSets = client.daemonSets().list("kube-system"); // Delete a DaemonSet client.daemonSets().delete("kube-system", "fluentd-elasticsearch");
Jobs
import io.elev8.resources.job.Job; import io.elev8.resources.job.JobSpec; // Create a simple batch Job final Job batchJob = Job.builder() .name("pi-calculation") .namespace("default") .spec(JobSpec.builder() .completions(1) .template(JobPodTemplateSpec.builder() .spec(PodSpec.builder() .container(Container.builder() .name("pi") .image("perl:5.34") .command(List.of("perl", "-Mbignum=bpi", "-wle", "print bpi(2000)")) .build()) .restartPolicy("Never") .build()) .build()) .build()) .build(); client.jobs().create(batchJob); // Create a parallel processing Job final Job parallelJob = Job.builder() .name("data-processor") .namespace("default") .spec(JobSpec.builder() .completions(10) .parallelism(3) .backoffLimit(4) .template(JobPodTemplateSpec.builder() .label("app", "processor") .spec(PodSpec.builder() .container(Container.builder() .name("processor") .image("data-processor:latest") .build()) .restartPolicy("OnFailure") .build()) .build()) .build()) .build(); client.jobs().create(parallelJob); // Create a Job with timeout and TTL final Job timedJob = Job.builder() .name("cleanup-job") .namespace("default") .spec(JobSpec.builder() .activeDeadlineSeconds(300L) .ttlSecondsAfterFinished(100) .template(JobPodTemplateSpec.builder() .spec(PodSpec.builder() .container(Container.builder() .name("cleanup") .image("cleanup:latest") .build()) .restartPolicy("Never") .build()) .build()) .build()) .build(); client.jobs().create(timedJob); // Get a Job final Job retrieved = client.jobs().get("default", "pi-calculation"); // List Jobs in namespace final List<Job> jobs = client.jobs().list("default"); // Delete a Job client.jobs().delete("default", "pi-calculation");
StatefulSets
import io.elev8.resources.statefulset.StatefulSet; import io.elev8.resources.statefulset.StatefulSetSpec; // Create a simple StatefulSet for a web application final StatefulSet webApp = StatefulSet.builder() .name("web") .namespace("default") .spec(StatefulSetSpec.builder() .serviceName("nginx") .replicas(3) .selector("app", "nginx") .template(StatefulSetPodTemplateSpec.builder() .label("app", "nginx") .spec(PodSpec.builder() .container(Container.builder() .name("nginx") .image("nginx:1.21") .addPort(80) .build()) .build()) .build()) .build()) .build(); client.statefulSets().create(webApp); // Create a StatefulSet with custom update strategy final StatefulSet database = StatefulSet.builder() .name("postgres") .namespace("default") .spec(StatefulSetSpec.builder() .serviceName("postgres") .replicas(3) .selector("app", "postgres") .updateStrategy("OnDelete") .podManagementPolicy("Parallel") .revisionHistoryLimit(5) .minReadySeconds(10) .template(StatefulSetPodTemplateSpec.builder() .label("app", "postgres") .spec(PodSpec.builder() .container(Container.builder() .name("postgres") .image("postgres:14") .build()) .build()) .build()) .build()) .build(); client.statefulSets().create(database); // Get a StatefulSet final StatefulSet retrieved = client.statefulSets().get("default", "web"); // List StatefulSets in namespace final List<StatefulSet> statefulSets = client.statefulSets().list("default"); // Delete a StatefulSet client.statefulSets().delete("default", "web");
ReplicaSets
import io.elev8.resources.replicaset.ReplicaSet; import io.elev8.resources.replicaset.ReplicaSetSpec; import io.elev8.resources.replicaset.ReplicaSetPodTemplateSpec; // Create a simple ReplicaSet final ReplicaSet replicaSet = ReplicaSet.builder() .name("nginx-replicaset") .namespace("default") .spec(ReplicaSetSpec.builder() .replicas(3) .selector("app", "nginx") .template(ReplicaSetPodTemplateSpec.builder() .label("app", "nginx") .spec(PodSpec.builder() .container(Container.builder() .name("nginx") .image("nginx:1.21") .addPort(80) .build()) .build()) .build()) .build()) .build(); client.replicaSets().create(replicaSet); // Create a ReplicaSet with minReadySeconds final ReplicaSet frontendReplicaSet = ReplicaSet.builder() .name("frontend") .namespace("default") .label("tier", "frontend") .spec(ReplicaSetSpec.builder() .replicas(5) .selector("app", "frontend") .selector("tier", "frontend") .minReadySeconds(30) .template(ReplicaSetPodTemplateSpec.builder() .label("app", "frontend") .label("tier", "frontend") .spec(PodSpec.builder() .container(Container.builder() .name("frontend") .image("frontend:v1") .build()) .build()) .build()) .build()) .build(); client.replicaSets().create(frontendReplicaSet); // Get a ReplicaSet final ReplicaSet retrieved = client.replicaSets().get("default", "nginx-replicaset"); // List ReplicaSets in namespace final List<ReplicaSet> replicaSets = client.replicaSets().list("default"); // Delete a ReplicaSet client.replicaSets().delete("default", "nginx-replicaset");
Ingress
import io.elev8.resources.ingress.Ingress; import io.elev8.resources.ingress.IngressSpec; import io.elev8.resources.ingress.IngressRule; import io.elev8.resources.ingress.HTTPIngressRuleValue; import io.elev8.resources.ingress.HTTPIngressPath; import io.elev8.resources.ingress.IngressBackend; import io.elev8.resources.ingress.IngressServiceBackend; import io.elev8.resources.ingress.ServiceBackendPort; import io.elev8.resources.ingress.IngressTLS; // Create a simple HTTP Ingress final Ingress simpleIngress = Ingress.builder() .name("example-ingress") .namespace("default") .spec(IngressSpec.builder() .ingressClassName("nginx") .rule(IngressRule.builder() .host("example.com") .http(HTTPIngressRuleValue.builder() .path(HTTPIngressPath.builder() .path("/") .pathType("Prefix") .backend(IngressBackend.builder() .service(IngressServiceBackend.builder() .name("web-service") .port(ServiceBackendPort.builder() .number(80) .build()) .build()) .build()) .build()) .build()) .build()) .build()) .build(); client.ingresses().create(simpleIngress); // Create an Ingress with multiple paths and TLS final Ingress tlsIngress = Ingress.builder() .name("app-ingress") .namespace("default") .label("app", "web") .spec(IngressSpec.builder() .ingressClassName("nginx") .tl(IngressTLS.builder() .host("app.example.com") .secretName("app-tls-secret") .build()) .rule(IngressRule.builder() .host("app.example.com") .http(HTTPIngressRuleValue.builder() .path(HTTPIngressPath.builder() .path("/api") .pathType("Prefix") .backend(IngressBackend.builder() .service(IngressServiceBackend.builder() .name("api-service") .port(ServiceBackendPort.builder() .number(8080) .build()) .build()) .build()) .build()) .path(HTTPIngressPath.builder() .path("/web") .pathType("Prefix") .backend(IngressBackend.builder() .service(IngressServiceBackend.builder() .name("web-service") .port(ServiceBackendPort.builder() .number(80) .build()) .build()) .build()) .build()) .build()) .build()) .build()) .build(); client.ingresses().create(tlsIngress); // Get an Ingress final Ingress retrieved = client.ingresses().get("default", "example-ingress"); // Check load balancer status if (retrieved.getStatus() != null && retrieved.getStatus().getLoadBalancer() != null) { retrieved.getStatus().getLoadBalancer().getIngress().forEach(lb -> { System.out.println("Load Balancer: " + (lb.getHostname() != null ? lb.getHostname() : lb.getIp())); }); } // List Ingresses in namespace final List<Ingress> ingresses = client.ingresses().list("default"); // Delete an Ingress client.ingresses().delete("default", "example-ingress");
kubectl equivalents:
# Create Ingress kubectl apply -f ingress.yaml # Get Ingress kubectl get ingress example-ingress # Describe Ingress (shows load balancer status) kubectl describe ingress example-ingress # Delete Ingress kubectl delete ingress example-ingress
CronJobs
import io.elev8.resources.cronjob.CronJob; import io.elev8.resources.cronjob.CronJobSpec; import io.elev8.resources.cronjob.CronJobJobTemplateSpec; // Create a simple CronJob that runs every 5 minutes final CronJob helloCron = CronJob.builder() .name("hello") .namespace("default") .spec(CronJobSpec.builder() .schedule("*/5 * * * *") .jobTemplate(CronJobJobTemplateSpec.builder() .spec(JobSpec.builder() .template(JobPodTemplateSpec.builder() .spec(PodSpec.builder() .container(Container.builder() .name("hello") .image("busybox:latest") .command(List.of("/bin/sh", "-c", "date; echo Hello from Kubernetes")) .build()) .restartPolicy("OnFailure") .build()) .build()) .build()) .build()) .build()) .build(); client.cronJobs().create(helloCron); // Create a CronJob with custom settings final CronJob backupJob = CronJob.builder() .name("database-backup") .namespace("default") .spec(CronJobSpec.builder() .schedule("0 2 * * *") .concurrencyPolicy("Forbid") .successfulJobsHistoryLimit(5) .failedJobsHistoryLimit(3) .startingDeadlineSeconds(300L) .suspend(false) .jobTemplate(CronJobJobTemplateSpec.builder() .label("app", "backup") .spec(JobSpec.builder() .template(JobPodTemplateSpec.builder() .spec(PodSpec.builder() .container(Container.builder() .name("backup") .image("backup-tool:latest") .build()) .restartPolicy("Never") .build()) .build()) .build()) .build()) .build()) .build(); client.cronJobs().create(backupJob); // Get a CronJob final CronJob retrieved = client.cronJobs().get("default", "hello"); // List CronJobs in namespace final List<CronJob> cronJobs = client.cronJobs().list("default"); // Delete a CronJob client.cronJobs().delete("default", "hello");
Namespaces
import io.elev8.resources.namespace.Namespace; import io.elev8.resources.namespace.NamespaceSpec; // Create a simple Namespace final Namespace namespace = Namespace.builder() .name("production") .build(); client.namespaces().create(namespace); // Create a Namespace with labels final Namespace devNamespace = Namespace.builder() .name("development") .label("env", "dev") .label("team", "backend") .build(); client.namespaces().create(devNamespace); // Create a Namespace with finalizers final Namespace protectedNamespace = Namespace.builder() .name("protected") .label("environment", "production") .addFinalizer("kubernetes") .addFinalizer("example.com/custom-finalizer") .build(); client.namespaces().create(protectedNamespace); // List all Namespaces final List<Namespace> namespaces = client.namespaces().listAllNamespaces(); // Get a specific Namespace final Namespace retrieved = client.namespaces().get("production"); // Delete a Namespace client.namespaces().delete("development");
ServiceAccounts
import io.elev8.resources.serviceaccount.ServiceAccount; import io.elev8.resources.serviceaccount.ServiceAccountSpec; import io.elev8.resources.LocalObjectReference; // Create a simple ServiceAccount final ServiceAccount serviceAccount = ServiceAccount.builder() .name("my-service-account") .namespace("default") .build(); client.serviceAccounts().create(serviceAccount); // Create a ServiceAccount with automount disabled final ServiceAccount noAutomount = ServiceAccount.builder() .name("no-automount-sa") .namespace("default") .spec(ServiceAccountSpec.builder() .automountServiceAccountToken(false) .build()) .build(); client.serviceAccounts().create(noAutomount); // Create a ServiceAccount with image pull secrets final ServiceAccount withImagePullSecrets = ServiceAccount.builder() .name("docker-sa") .namespace("default") .label("app", "backend") .spec(ServiceAccountSpec.builder() .automountServiceAccountToken(true) .imagePullSecret(LocalObjectReference.builder() .name("docker-registry-secret") .build()) .imagePullSecret(LocalObjectReference.builder() .name("ghcr-secret") .build()) .build()) .build(); client.serviceAccounts().create(withImagePullSecrets); // Get a ServiceAccount final ServiceAccount retrieved = client.serviceAccounts().get("default", "my-service-account"); // List ServiceAccounts in a namespace final List<ServiceAccount> serviceAccounts = client.serviceAccounts().list("default"); // Delete a ServiceAccount client.serviceAccounts().delete("default", "my-service-account");
kubectl equivalents:
# Create ServiceAccount kubectl apply -f serviceaccount.yaml # Get ServiceAccount kubectl get serviceaccount my-service-account -n default # Describe ServiceAccount (shows secrets and tokens) kubectl describe serviceaccount my-service-account -n default # Delete ServiceAccount kubectl delete serviceaccount my-service-account -n default
Roles
import io.elev8.resources.role.Role; import io.elev8.resources.role.RoleSpec; import io.elev8.resources.role.PolicyRule; // Create a Role for reading pods final Role podReader = Role.builder() .name("pod-reader") .namespace("default") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .verb("get") .verb("list") .verb("watch") .build()) .build()) .build(); client.roles().create(podReader); // Create a Role with multiple resource permissions final Role developerRole = Role.builder() .name("developer") .namespace("default") .label("team", "backend") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .resource("services") .verb("get") .verb("list") .verb("watch") .build()) .rule(PolicyRule.builder() .apiGroup("apps") .resource("deployments") .verb("get") .verb("list") .verb("watch") .build()) .build()) .build(); client.roles().create(developerRole); // Create a Role with specific resource names final Role secretReader = Role.builder() .name("secret-reader") .namespace("default") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("secrets") .verb("get") .resourceName("app-config") .resourceName("database-credentials") .build()) .build()) .build(); client.roles().create(secretReader); // Create an admin Role with wildcard permissions final Role namespaceAdmin = Role.builder() .name("namespace-admin") .namespace("production") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("*") .resource("*") .verb("*") .build()) .build()) .build(); client.roles().create(namespaceAdmin); // Get a Role final Role retrieved = client.roles().get("default", "pod-reader"); // List Roles in a namespace final List<Role> roles = client.roles().list("default"); // Delete a Role client.roles().delete("default", "pod-reader");
kubectl equivalents:
# Create Role kubectl apply -f role.yaml # Get Role kubectl get role pod-reader -n default # Describe Role (shows policy rules) kubectl describe role pod-reader -n default # Delete Role kubectl delete role pod-reader -n default
RoleBindings
import io.elev8.resources.rolebinding.RoleBinding; import io.elev8.resources.rolebinding.RoleBindingSpec; import io.elev8.resources.rolebinding.Subject; import io.elev8.resources.rolebinding.RoleRef; // Bind a Role to a ServiceAccount final RoleBinding podReaderBinding = RoleBinding.builder() .name("read-pods") .namespace("default") .spec(RoleBindingSpec.builder() .subject(Subject.builder() .kind("ServiceAccount") .name("my-service-account") .namespace("default") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("Role") .name("pod-reader") .build()) .build()) .build(); client.roleBindings().create(podReaderBinding); // Bind a Role to a User final RoleBinding userBinding = RoleBinding.builder() .name("developer-binding") .namespace("default") .spec(RoleBindingSpec.builder() .subject(Subject.builder() .kind("User") .name("jane@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("Role") .name("developer") .build()) .build()) .build(); client.roleBindings().create(userBinding); // Bind a Role to a Group final RoleBinding groupBinding = RoleBinding.builder() .name("team-binding") .namespace("production") .spec(RoleBindingSpec.builder() .subject(Subject.builder() .kind("Group") .name("developers") .apiGroup("rbac.authorization.k8s.io") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("Role") .name("namespace-admin") .build()) .build()) .build(); client.roleBindings().create(groupBinding); // Bind a ClusterRole to subjects in a namespace (grants namespace-scoped permissions) final RoleBinding viewBinding = RoleBinding.builder() .name("view-binding") .namespace("default") .spec(RoleBindingSpec.builder() .subject(Subject.builder() .kind("ServiceAccount") .name("viewer-sa") .namespace("default") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("ClusterRole") .name("view") .build()) .build()) .build(); client.roleBindings().create(viewBinding); // Bind to multiple subjects final RoleBinding multiSubjectBinding = RoleBinding.builder() .name("multi-reader") .namespace("default") .spec(RoleBindingSpec.builder() .subject(Subject.builder() .kind("User") .name("alice@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .subject(Subject.builder() .kind("User") .name("bob@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .subject(Subject.builder() .kind("ServiceAccount") .name("app-sa") .namespace("default") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("Role") .name("pod-reader") .build()) .build()) .build(); client.roleBindings().create(multiSubjectBinding); // Get a RoleBinding final RoleBinding retrieved = client.roleBindings().get("default", "read-pods"); // List RoleBindings in a namespace final List<RoleBinding> roleBindings = client.roleBindings().list("default"); // Delete a RoleBinding client.roleBindings().delete("default", "read-pods");
kubectl equivalents:
# Create RoleBinding kubectl apply -f rolebinding.yaml # Get RoleBinding kubectl get rolebinding read-pods -n default # Describe RoleBinding (shows subjects and role reference) kubectl describe rolebinding read-pods -n default # Delete RoleBinding kubectl delete rolebinding read-pods -n default # Create RoleBinding using kubectl create kubectl create rolebinding read-pods \ --role=pod-reader \ --serviceaccount=default:my-service-account \ -n default
ClusterRoles
import io.elev8.resources.clusterrole.ClusterRole; import io.elev8.resources.role.RoleSpec; import io.elev8.resources.role.PolicyRule; // Create a ClusterRole for reading pods cluster-wide final ClusterRole podReader = ClusterRole.builder() .name("pod-reader") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .verb("get") .verb("list") .verb("watch") .build()) .build()) .build(); client.clusterRoles().create(podReader); // Create a ClusterRole with multiple resource permissions final ClusterRole adminRole = ClusterRole.builder() .name("resource-admin") .label("type", "admin") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .resource("services") .resource("configmaps") .verb("get") .verb("list") .verb("watch") .verb("create") .verb("update") .verb("delete") .build()) .rule(PolicyRule.builder() .apiGroup("apps") .resource("deployments") .resource("statefulsets") .verb("*") .build()) .build()) .build(); client.clusterRoles().create(adminRole); // Create a ClusterRole with specific resource names final ClusterRole secretReader = ClusterRole.builder() .name("specific-secret-reader") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("secrets") .verb("get") .resourceName("important-secret") .resourceName("database-credentials") .build()) .build()) .build(); client.clusterRoles().create(secretReader); // Create a cluster-admin ClusterRole with wildcard permissions final ClusterRole clusterAdmin = ClusterRole.builder() .name("cluster-admin") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("*") .resource("*") .verb("*") .build()) .build()) .build(); client.clusterRoles().create(clusterAdmin); // Get a ClusterRole (no namespace needed - cluster-scoped) final ClusterRole retrieved = client.clusterRoles().get("pod-reader"); // List all ClusterRoles final List<ClusterRole> clusterRoles = client.clusterRoles().list(); // Delete a ClusterRole client.clusterRoles().delete("pod-reader");
kubectl equivalents:
# Create ClusterRole kubectl apply -f clusterrole.yaml # Get ClusterRole (no namespace needed) kubectl get clusterrole pod-reader # Describe ClusterRole (shows policy rules) kubectl describe clusterrole pod-reader # Delete ClusterRole kubectl delete clusterrole pod-reader # Create ClusterRole using kubectl create kubectl create clusterrole pod-reader \ --verb=get,list,watch \ --resource=pods
ClusterRoleBindings
import io.elev8.resources.clusterrolebinding.ClusterRoleBinding; import io.elev8.resources.clusterrolebinding.ClusterRoleBindingSpec; import io.elev8.resources.rolebinding.Subject; import io.elev8.resources.rolebinding.RoleRef; // Bind a ClusterRole to a user cluster-wide final ClusterRoleBinding adminBinding = ClusterRoleBinding.builder() .name("cluster-admin-binding") .spec(ClusterRoleBindingSpec.builder() .subject(Subject.builder() .kind("User") .name("admin@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("ClusterRole") .name("cluster-admin") .build()) .build()) .build(); client.clusterRoleBindings().create(adminBinding); // Bind a ClusterRole to a ServiceAccount cluster-wide final ClusterRoleBinding saBinding = ClusterRoleBinding.builder() .name("system-sa-binding") .spec(ClusterRoleBindingSpec.builder() .subject(Subject.builder() .kind("ServiceAccount") .name("system-service-account") .namespace("kube-system") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("ClusterRole") .name("cluster-admin") .build()) .build()) .build(); client.clusterRoleBindings().create(saBinding); // Bind a ClusterRole to a Group final ClusterRoleBinding groupBinding = ClusterRoleBinding.builder() .name("developers-view") .label("team", "developers") .spec(ClusterRoleBindingSpec.builder() .subject(Subject.builder() .kind("Group") .name("developers") .apiGroup("rbac.authorization.k8s.io") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("ClusterRole") .name("view") .build()) .build()) .build(); client.clusterRoleBindings().create(groupBinding); // Bind to multiple subjects cluster-wide final ClusterRoleBinding multiBinding = ClusterRoleBinding.builder() .name("multi-admin-binding") .spec(ClusterRoleBindingSpec.builder() .subject(Subject.builder() .kind("User") .name("alice@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .subject(Subject.builder() .kind("User") .name("bob@example.com") .apiGroup("rbac.authorization.k8s.io") .build()) .subject(Subject.builder() .kind("Group") .name("system:masters") .apiGroup("rbac.authorization.k8s.io") .build()) .roleRef(RoleRef.builder() .apiGroup("rbac.authorization.k8s.io") .kind("ClusterRole") .name("cluster-admin") .build()) .build()) .build(); client.clusterRoleBindings().create(multiBinding); // Get a ClusterRoleBinding (no namespace needed - cluster-scoped) final ClusterRoleBinding retrieved = client.clusterRoleBindings().get("cluster-admin-binding"); // List all ClusterRoleBindings final List<ClusterRoleBinding> bindings = client.clusterRoleBindings().list(); // Delete a ClusterRoleBinding client.clusterRoleBindings().delete("cluster-admin-binding");
kubectl equivalents:
# Create ClusterRoleBinding kubectl apply -f clusterrolebinding.yaml # Get ClusterRoleBinding (no namespace needed) kubectl get clusterrolebinding cluster-admin-binding # Describe ClusterRoleBinding (shows subjects and role reference) kubectl describe clusterrolebinding cluster-admin-binding # Delete ClusterRoleBinding kubectl delete clusterrolebinding cluster-admin-binding # Create ClusterRoleBinding using kubectl create kubectl create clusterrolebinding cluster-admin-binding \ --clusterrole=cluster-admin \ --user=admin@example.com # Create ClusterRoleBinding for a ServiceAccount kubectl create clusterrolebinding system-sa-binding \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:system-service-account
NetworkPolicies
import io.elev8.resources.networkpolicy.NetworkPolicy; import io.elev8.resources.networkpolicy.NetworkPolicySpec; import io.elev8.resources.networkpolicy.NetworkPolicyIngressRule; import io.elev8.resources.networkpolicy.NetworkPolicyEgressRule; import io.elev8.resources.networkpolicy.NetworkPolicyPeer; import io.elev8.resources.networkpolicy.NetworkPolicyPort; import io.elev8.resources.networkpolicy.IPBlock; import io.elev8.resources.LabelSelector; // Create a deny-all ingress policy (baseline security) final NetworkPolicy denyAll = NetworkPolicy.builder() .name("deny-all-ingress") .namespace("default") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder().build()) // Empty selector = all pods .policyType("Ingress") .build()) .build(); client.networkPolicies().create(denyAll); // Create a NetworkPolicy allowing database access from specific pods final NetworkPolicy allowDbAccess = NetworkPolicy.builder() .name("allow-db-access") .namespace("default") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder() .matchLabel("role", "db") .build()) .policyType("Ingress") .ingressRule(NetworkPolicyIngressRule.builder() .from(NetworkPolicyPeer.builder() .podSelector(LabelSelector.builder() .matchLabel("role", "frontend") .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(5432) .build()) .build()) .build()) .build(); client.networkPolicies().create(allowDbAccess); // Create a NetworkPolicy with namespace selector final NetworkPolicy allowFromProd = NetworkPolicy.builder() .name("allow-from-production") .namespace("default") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder() .matchLabel("app", "backend") .build()) .policyType("Ingress") .ingressRule(NetworkPolicyIngressRule.builder() .from(NetworkPolicyPeer.builder() .namespaceSelector(LabelSelector.builder() .matchLabel("environment", "production") .build()) .build()) .build()) .build()) .build(); client.networkPolicies().create(allowFromProd); // Create a NetworkPolicy with IP block (allow specific CIDR) final NetworkPolicy allowExternalCidr = NetworkPolicy.builder() .name("allow-external") .namespace("default") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder() .matchLabel("role", "api") .build()) .policyType("Ingress") .ingressRule(NetworkPolicyIngressRule.builder() .from(NetworkPolicyPeer.builder() .ipBlock(IPBlock.builder() .cidr("172.17.0.0/16") .except("172.17.1.0/24") .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(443) .build()) .build()) .build()) .build(); client.networkPolicies().create(allowExternalCidr); // Create a NetworkPolicy with egress rules (restrict outbound traffic) final NetworkPolicy restrictEgress = NetworkPolicy.builder() .name("restrict-egress") .namespace("default") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder() .matchLabel("app", "web") .build()) .policyType("Egress") .egressRule(NetworkPolicyEgressRule.builder() .to(NetworkPolicyPeer.builder() .podSelector(LabelSelector.builder() .matchLabel("role", "db") .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(5432) .build()) .build()) .egressRule(NetworkPolicyEgressRule.builder() .to(NetworkPolicyPeer.builder() .ipBlock(IPBlock.builder() .cidr("0.0.0.0/0") .except("169.254.169.254/32") // Block EC2 metadata endpoint .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(443) .build()) .build()) .build()) .build(); client.networkPolicies().create(restrictEgress); // Create a NetworkPolicy with both ingress and egress final NetworkPolicy fullPolicy = NetworkPolicy.builder() .name("full-network-policy") .namespace("default") .label("tier", "security") .spec(NetworkPolicySpec.builder() .podSelector(LabelSelector.builder() .matchLabel("app", "payment") .build()) .policyType("Ingress") .policyType("Egress") .ingressRule(NetworkPolicyIngressRule.builder() .from(NetworkPolicyPeer.builder() .podSelector(LabelSelector.builder() .matchLabel("app", "frontend") .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(8080) .build()) .build()) .egressRule(NetworkPolicyEgressRule.builder() .to(NetworkPolicyPeer.builder() .podSelector(LabelSelector.builder() .matchLabel("app", "database") .build()) .build()) .port(NetworkPolicyPort.builder() .protocol("TCP") .port(5432) .build()) .build()) .build()) .build(); client.networkPolicies().create(fullPolicy); // Get a NetworkPolicy final NetworkPolicy retrieved = client.networkPolicies().get("default", "allow-db-access"); // List NetworkPolicies in a namespace final List<NetworkPolicy> policies = client.networkPolicies().list("default"); // Delete a NetworkPolicy client.networkPolicies().delete("default", "deny-all-ingress");
kubectl equivalents:
# Create NetworkPolicy kubectl apply -f networkpolicy.yaml # Get NetworkPolicy kubectl get networkpolicy allow-db-access -n default # Describe NetworkPolicy (shows ingress/egress rules) kubectl describe networkpolicy allow-db-access -n default # Delete NetworkPolicy kubectl delete networkpolicy allow-db-access -n default # List NetworkPolicies kubectl get networkpolicies -n default
HorizontalPodAutoscalers
import io.elev8.resources.horizontalpodautoscaler.*; // Simple CPU-based autoscaling final HorizontalPodAutoscaler cpuHpa = HorizontalPodAutoscaler.builder() .name("php-apache-hpa") .namespace("default") .spec(HorizontalPodAutoscalerSpec.builder() .scaleTargetRef(CrossVersionObjectReference.builder() .apiVersion("apps/v1") .kind("Deployment") .name("php-apache") .build()) .minReplicas(1) .maxReplicas(10) .metric(MetricSpec.builder() .type("Resource") .resource(ResourceMetricSource.builder() .name("cpu") .target(MetricTarget.builder() .type("Utilization") .averageUtilization(50) .build()) .build()) .build()) .build()) .build(); client.horizontalPodAutoscalers().create(cpuHpa); // Multi-metric HPA (CPU + Memory) final HorizontalPodAutoscaler multiMetricHpa = HorizontalPodAutoscaler.builder() .name("multi-metric-hpa") .namespace("default") .spec(HorizontalPodAutoscalerSpec.builder() .scaleTargetRef(CrossVersionObjectReference.builder() .apiVersion("apps/v1") .kind("Deployment") .name("my-app") .build()) .minReplicas(2) .maxReplicas(20) .metric(MetricSpec.builder() .type("Resource") .resource(ResourceMetricSource.builder() .name("cpu") .target(MetricTarget.builder() .type("Utilization") .averageUtilization(70) .build()) .build()) .build()) .metric(MetricSpec.builder() .type("Resource") .resource(ResourceMetricSource.builder() .name("memory") .target(MetricTarget.builder() .type("Utilization") .averageUtilization(80) .build()) .build()) .build()) .build()) .build(); client.horizontalPodAutoscalers().create(multiMetricHpa); // Get and check status final HorizontalPodAutoscaler hpa = client.horizontalPodAutoscalers() .get("default", "php-apache-hpa"); System.out.println("Current: " + hpa.getStatus().getCurrentReplicas()); System.out.println("Desired: " + hpa.getStatus().getDesiredReplicas()); // List HPAs final List<HorizontalPodAutoscaler> hpas = client.horizontalPodAutoscalers().list("default"); // Delete HPA client.horizontalPodAutoscalers().delete("default", "php-apache-hpa");
kubectl equivalents:
# Create HPA kubectl apply -f hpa.yaml # Create HPA for deployment (shorthand) kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10 # Get HPA kubectl get hpa php-apache-hpa -n default # Describe HPA (shows current/desired replicas, metrics) kubectl describe hpa php-apache-hpa -n default # Delete HPA kubectl delete hpa php-apache-hpa -n default
VerticalPodAutoscalers
NOTE: VPA is a Custom Resource Definition (CRD) and must be installed in your cluster before use. See Kubernetes VPA Installation.
import io.elev8.resources.verticalpodautoscaler.*; import io.elev8.resources.horizontalpodautoscaler.CrossVersionObjectReference; // Create VPA with recommendation-only mode (safe for production) final VerticalPodAutoscaler vpaOff = VerticalPodAutoscaler.builder() .name("my-app-vpa") .namespace("default") .spec(VerticalPodAutoscalerSpec.builder() .targetRef(CrossVersionObjectReference.builder() .apiVersion("apps/v1") .kind("Deployment") .name("my-app") .build()) .updatePolicy(VPAUpdatePolicy.builder() .updateMode("Off") // Only provide recommendations, don't update pods .build()) .resourcePolicy(VPAResourcePolicy.builder() .containerPolicy(VPAContainerResourcePolicy.builder() .containerName("*") // Apply to all containers .minAllowed(Map.of("cpu", "100m", "memory", "128Mi")) .maxAllowed(Map.of("cpu", "2", "memory", "2Gi")) .build()) .build()) .build()) .build(); client.verticalPodAutoscalers().create(vpaOff); // Create VPA with auto-update mode (updates pods automatically) final VerticalPodAutoscaler vpaAuto = VerticalPodAutoscaler.builder() .name("auto-vpa") .namespace("default") .spec(VerticalPodAutoscalerSpec.builder() .targetRef(CrossVersionObjectReference.builder() .apiVersion("apps/v1") .kind("Deployment") .name("web-app") .build()) .updatePolicy(VPAUpdatePolicy.builder() .updateMode("Auto") // Automatically update pods (causes restarts) .minReplicas(2) // Ensure at least 2 replicas during updates .build()) .build()) .build(); client.verticalPodAutoscalers().create(vpaAuto); // Get VPA and check recommendations final VerticalPodAutoscaler vpa = client.verticalPodAutoscalers() .get("default", "my-app-vpa"); if (vpa.getStatus() != null && vpa.getStatus().getRecommendation() != null) { vpa.getStatus().getRecommendation().getContainerRecommendations() .forEach(rec -> { System.out.println("Container: " + rec.getContainerName()); System.out.println("Target CPU: " + rec.getTarget().get("cpu")); System.out.println("Target Memory: " + rec.getTarget().get("memory")); System.out.println("Lower Bound CPU: " + rec.getLowerBound().get("cpu")); System.out.println("Upper Bound CPU: " + rec.getUpperBound().get("cpu")); }); } // List VPAs final List<VerticalPodAutoscaler> vpas = client.verticalPodAutoscalers().list("default"); // Delete VPA client.verticalPodAutoscalers().delete("default", "my-app-vpa");
kubectl equivalents:
# Install VPA CRD (required first-time setup) kubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vertical-pod-autoscaler.yaml # Create VPA kubectl apply -f vpa.yaml # Get VPA kubectl get vpa my-app-vpa -n default # Describe VPA (shows recommendations) kubectl describe vpa my-app-vpa -n default # Delete VPA kubectl delete vpa my-app-vpa -n default
ResourceQuotas
import io.elev8.resources.resourcequota.*; // Create ResourceQuota with compute limits final ResourceQuota computeQuota = ResourceQuota.builder() .name("compute-quota") .namespace("my-namespace") .spec(ResourceQuotaSpec.builder() .hardLimit("requests.cpu", "10") .hardLimit("requests.memory", "20Gi") .hardLimit("limits.cpu", "20") .hardLimit("limits.memory", "40Gi") .hardLimit("pods", "50") .build()) .build(); client.resourceQuotas().create(computeQuota); // Create ResourceQuota with object count limits final ResourceQuota objectQuota = ResourceQuota.builder() .name("object-quota") .namespace("my-namespace") .spec(ResourceQuotaSpec.builder() .hardLimit("pods", "100") .hardLimit("services", "20") .hardLimit("secrets", "30") .hardLimit("configmaps", "25") .hardLimit("persistentvolumeclaims", "15") .build()) .build(); client.resourceQuotas().create(objectQuota); // Create ResourceQuota with scopes final ResourceQuota scopedQuota = ResourceQuota.builder() .name("terminating-quota") .namespace("my-namespace") .spec(ResourceQuotaSpec.builder() .hardLimit("pods", "10") .scope("Terminating") // Only count terminating pods .build()) .build(); client.resourceQuotas().create(scopedQuota); // Get quota and check usage final ResourceQuota quota = client.resourceQuotas() .get("my-namespace", "compute-quota"); System.out.println("CPU Used: " + quota.getStatus().getUsed().get("requests.cpu")); System.out.println("CPU Limit: " + quota.getStatus().getHard().get("requests.cpu")); // List quotas final List<ResourceQuota> quotas = client.resourceQuotas().list("my-namespace"); // Delete quota client.resourceQuotas().delete("my-namespace", "compute-quota");
kubectl equivalents:
# Create ResourceQuota kubectl apply -f resourcequota.yaml # Get ResourceQuota kubectl get resourcequota compute-quota -n my-namespace # Describe ResourceQuota (shows used vs hard limits) kubectl describe resourcequota compute-quota -n my-namespace # Delete ResourceQuota kubectl delete resourcequota compute-quota -n my-namespace
PersistentVolumes
import io.elev8.resources.persistentvolume.PersistentVolume; import io.elev8.resources.persistentvolume.PersistentVolumeSpec; import io.elev8.resources.persistentvolume.HostPathVolumeSource; import io.elev8.resources.persistentvolume.NFSVolumeSource; import io.elev8.resources.persistentvolume.AWSElasticBlockStoreVolumeSource; // Create a HostPath PersistentVolume final PersistentVolume hostPathPV = PersistentVolume.builder() .name("local-pv") .label("type", "local") .spec(PersistentVolumeSpec.builder() .storageClassName("manual") .capacity("10Gi") .accessMode("ReadWriteOnce") .hostPath(HostPathVolumeSource.builder() .path("/mnt/data") .type("DirectoryOrCreate") .build()) .build()) .build(); client.persistentVolumes().create(hostPathPV); // Create an NFS PersistentVolume final PersistentVolume nfsPV = PersistentVolume.builder() .name("nfs-pv") .label("type", "nfs") .spec(PersistentVolumeSpec.builder() .storageClassName("nfs") .capacity("50Gi") .accessMode("ReadWriteMany") .persistentVolumeReclaimPolicy("Retain") .nfs(NFSVolumeSource.builder() .server("nfs-server.example.com") .path("/exports/data") .readOnly(false) .build()) .build()) .build(); client.persistentVolumes().create(nfsPV); // Create an AWS EBS PersistentVolume final PersistentVolume ebsPV = PersistentVolume.builder() .name("ebs-pv") .label("type", "ebs") .spec(PersistentVolumeSpec.builder() .storageClassName("gp2") .capacity("100Gi") .accessMode("ReadWriteOnce") .awsElasticBlockStore(AWSElasticBlockStoreVolumeSource.builder() .volumeID("vol-0123456789abcdef0") .fsType("ext4") .build()) .build()) .build(); client.persistentVolumes().create(ebsPV); // Get a PersistentVolume (note: no namespace needed, cluster-scoped) final PersistentVolume retrieved = client.persistentVolumes().get("local-pv"); // List all PersistentVolumes final List<PersistentVolume> pvs = client.persistentVolumes().list(); // Delete a PersistentVolume client.persistentVolumes().delete("local-pv");
kubectl equivalents:
# Create PersistentVolume kubectl apply -f persistentvolume.yaml # Get PersistentVolume (no namespace needed) kubectl get pv local-pv # List all PersistentVolumes kubectl get pv # Describe PersistentVolume (shows capacity, access modes, status) kubectl describe pv local-pv # Delete PersistentVolume kubectl delete pv local-pv
PersistentVolumeClaims
import io.elev8.resources.persistentvolumeclaim.PersistentVolumeClaim; import io.elev8.resources.persistentvolumeclaim.PersistentVolumeClaimSpec; import io.elev8.resources.ResourceRequirements; // Create a simple PersistentVolumeClaim final PersistentVolumeClaim pvc = PersistentVolumeClaim.builder() .name("my-pvc") .namespace("default") .spec(PersistentVolumeClaimSpec.builder() .accessMode("ReadWriteOnce") .resources(ResourceRequirements.builder() .request("storage", "10Gi") .build()) .build()) .build(); client.persistentVolumeClaims().create(pvc); // Create a PVC with a specific StorageClass final PersistentVolumeClaim ebsPVC = PersistentVolumeClaim.builder() .name("ebs-claim") .namespace("default") .label("app", "database") .spec(PersistentVolumeClaimSpec.builder() .accessMode("ReadWriteOnce") .storageClassName("gp2") .resources(ResourceRequirements.builder() .request("storage", "100Gi") .build()) .build()) .build(); client.persistentVolumeClaims().create(ebsPVC); // Create a shared PVC with multiple access modes final PersistentVolumeClaim sharedPVC = PersistentVolumeClaim.builder() .name("shared-data") .namespace("default") .spec(PersistentVolumeClaimSpec.builder() .accessMode("ReadWriteMany") .storageClassName("nfs") .resources(ResourceRequirements.builder() .request("storage", "50Gi") .build()) .build()) .build(); client.persistentVolumeClaims().create(sharedPVC); // Create a block mode PVC final PersistentVolumeClaim blockPVC = PersistentVolumeClaim.builder() .name("block-storage") .namespace("default") .spec(PersistentVolumeClaimSpec.builder() .accessMode("ReadWriteOnce") .volumeMode("Block") .storageClassName("fast-ssd") .resources(ResourceRequirements.builder() .request("storage", "20Gi") .build()) .build()) .build(); client.persistentVolumeClaims().create(blockPVC); // Get a PersistentVolumeClaim final PersistentVolumeClaim retrieved = client.persistentVolumeClaims() .get("default", "my-pvc"); // List PersistentVolumeClaims in a namespace final List<PersistentVolumeClaim> pvcs = client.persistentVolumeClaims() .list("default"); // Delete a PersistentVolumeClaim client.persistentVolumeClaims().delete("default", "my-pvc");
kubectl equivalents:
# Create PersistentVolumeClaim kubectl apply -f persistentvolumeclaim.yaml # Get PersistentVolumeClaim kubectl get pvc my-pvc -n default # List all PersistentVolumeClaims kubectl get pvc -n default # Describe PersistentVolumeClaim (shows capacity, status, volume) kubectl describe pvc my-pvc -n default # Delete PersistentVolumeClaim kubectl delete pvc my-pvc -n default
EKS Access Entries
import io.elev8.auth.accessentries.AccessEntry; import io.elev8.auth.accessentries.AccessEntryManager; // Create access entry manager final AccessEntryManager manager = client.accessEntries(); // Create an access entry final AccessEntry entry = AccessEntry.builder() .principalArn("arn:aws:iam::123456789012:role/Developer") .kubernetesGroups(List.of("developers")) .type("STANDARD") .build(); manager.create(entry); // List access entries final List<AccessEntry> entries = manager.list(); // Migrate from aws-auth ConfigMap manager.migrateFromConfigMap();
Watch API
Watch resources in real-time for changes:
import io.elev8.core.watch.WatchEvent; import io.elev8.core.watch.WatchOptions; import io.elev8.core.watch.Watcher; // Watch pods in a namespace final WatchOptions options = WatchOptions.defaults(); client.pods().watch("default", options, new Watcher<Pod>() { @Override public void onEvent(final WatchEvent<Pod> event) { System.out.println("Event: " + event.getType()); System.out.println("Pod: " + event.getObject().getName()); if (event.isAdded()) { // Handle pod added } else if (event.isModified()) { // Handle pod modified } else if (event.isDeleted()) { // Handle pod deleted } } @Override public void onError(final Exception exception) { System.err.println("Watch error: " + exception.getMessage()); } @Override public void onClose() { System.out.println("Watch closed"); } }); // Watch with label selector final WatchOptions labelOptions = WatchOptions.withLabelSelector("app=myapp"); client.pods().watch("default", labelOptions, watcher); // Watch with field selector final WatchOptions fieldOptions = WatchOptions.withFieldSelector("status.phase=Running"); client.pods().watch("default", fieldOptions, watcher); // Watch all namespaces client.pods().watchAllNamespaces(options, watcher); // Resume watch from a specific resource version final WatchOptions resumeOptions = WatchOptions.from("12345"); client.pods().watch("default", resumeOptions, watcher); // Watch cluster-scoped resources client.namespaces().watch(options, watcher);
Pod Log Streaming
Stream logs from pod containers in real-time:
import io.elev8.core.logs.LogOptions; import io.elev8.core.logs.LogWatch; // Stream logs with default options final LogOptions options = LogOptions.defaults(); client.pods().logs("default", "my-pod", options, new LogWatch() { @Override public void onLog(final String line) { System.out.println(line); } @Override public void onError(final Exception exception) { System.err.println("Log streaming error: " + exception.getMessage()); } @Override public void onClose() { System.out.println("Log stream closed"); } }); // Follow logs (tail -f behavior) final LogOptions followOptions = LogOptions.follow(); client.pods().logs("default", "my-pod", followOptions, logWatch); // Tail last 100 lines final LogOptions tailOptions = LogOptions.tail(100); client.pods().logs("default", "my-pod", tailOptions, logWatch); // Follow with timestamps final LogOptions timestampOptions = LogOptions.followWithTimestamps(); client.pods().logs("default", "my-pod", timestampOptions, logWatch); // Logs from specific container (multi-container pods) final LogOptions containerOptions = LogOptions.forContainer("nginx"); client.pods().logs("default", "my-pod", containerOptions, logWatch); // Or use convenience method client.pods().logs("default", "my-pod", "nginx", options, logWatch); // Logs from previous terminated container final LogOptions previousOptions = LogOptions.previous(); client.pods().logs("default", "my-pod", previousOptions, logWatch); // Advanced options final LogOptions advancedOptions = LogOptions.builder() .follow(true) .tailLines(50) .timestamps(true) .sinceSeconds(3600) // Last hour .container("app") .build(); client.pods().logs("default", "my-pod", advancedOptions, logWatch);
Patch Operations
Efficiently update resources with partial changes using JSON Patch, JSON Merge Patch, or Strategic Merge Patch:
import io.elev8.core.patch.PatchOptions; import io.elev8.core.patch.PatchType; // Strategic Merge Patch (Kubernetes-specific, default) final String strategicPatch = "{\"spec\": {\"replicas\": 5}}"; final Deployment patched = client.deployments().patch( "default", "my-app", PatchOptions.strategicMergePatch(), strategicPatch ); // JSON Merge Patch (RFC 7396 - simplest) final String mergePatch = "{\"metadata\": {\"labels\": {\"env\": \"prod\"}}}"; client.deployments().patch( "default", "my-app", PatchOptions.mergePatch(), mergePatch ); // JSON Patch (RFC 6902 - most precise) final String jsonPatch = "[" + "{\"op\": \"replace\", \"path\": \"/spec/replicas\", \"value\": 5}," + "{\"op\": \"add\", \"path\": \"/metadata/labels/env\", \"value\": \"prod\"}" + "]"; client.deployments().patch( "default", "my-app", PatchOptions.jsonPatch(), jsonPatch ); // Patch with dry-run (test without applying) final PatchOptions dryRunOptions = PatchOptions.dryRun(PatchType.STRATEGIC_MERGE_PATCH); client.deployments().patch("default", "my-app", dryRunOptions, patch); // Patch with field manager (for Server-side Apply compatibility) final PatchOptions fieldManagerOptions = PatchOptions.builder() .patchType(PatchType.STRATEGIC_MERGE_PATCH) .fieldManager("elev8-client") .build(); client.deployments().patch("default", "my-app", fieldManagerOptions, patch); // Force patch (take ownership of conflicting fields) final PatchOptions forceOptions = PatchOptions.builder() .patchType(PatchType.STRATEGIC_MERGE_PATCH) .fieldManager("elev8-client") .force(true) .build(); client.deployments().patch("default", "my-app", forceOptions, patch); // Patch cluster-scoped resources final String namespacePatch = "{\"metadata\": {\"labels\": {\"team\": \"platform\"}}}"; client.namespaces().patch("production", PatchOptions.defaults(), namespacePatch);
Server-side Apply
Declaratively manage resources with field-level ownership tracking (recommended approach for GitOps):
import io.elev8.core.patch.ApplyOptions; // Server-side Apply with field manager (required) final ApplyOptions options = ApplyOptions.of("elev8-client"); final String deploymentManifest = """ { "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "my-app", "namespace": "default" }, "spec": { "replicas": 3, "selector": { "matchLabels": {"app": "my-app"} }, "template": { "metadata": {"labels": {"app": "my-app"}}, "spec": { "containers": [{ "name": "app", "image": "nginx:latest" }] } } } } """; final Deployment applied = client.deployments().apply( "default", "my-app", options, deploymentManifest ); // Dry-run to test before applying final ApplyOptions dryRunOptions = ApplyOptions.dryRun("elev8-client"); client.deployments().apply("default", "my-app", dryRunOptions, manifest); // Force apply (take ownership of conflicting fields) final ApplyOptions forceOptions = ApplyOptions.force("elev8-client"); client.deployments().apply("default", "my-app", forceOptions, manifest); // Builder pattern for advanced options final ApplyOptions advancedOptions = ApplyOptions.builder() .fieldManager("my-operator") .force(false) .dryRun(false) .build(); client.deployments().apply("default", "my-app", advancedOptions, manifest); // Apply cluster-scoped resources final String namespaceManifest = """ { "apiVersion": "v1", "kind": "Namespace", "metadata": { "name": "production", "labels": {"environment": "prod"} } } """; client.namespaces().apply("production", options, namespaceManifest);
Benefits of Server-side Apply:
- Field-level ownership tracking prevents conflicts
- Declarative management (specify desired state, not operations)
- Multiple controllers can manage different fields safely
- Automatic conflict detection and resolution
- Recommended for GitOps and infrastructure-as-code
Exec into Pods
Execute commands inside running containers with bidirectional streaming via WebSocket:
import io.elev8.core.exec.ExecOptions; import io.elev8.core.exec.ExecWatch; // Execute a simple command final ExecOptions options = ExecOptions.of(new String[]{"/bin/sh", "-c", "ls -la"}); client.pods().exec("default", "my-pod", options, new ExecWatch() { @Override public void onStdout(String data) { System.out.print(data); } @Override public void onStderr(String data) { System.err.print(data); } @Override public void onError(String error) { System.err.println("Error: " + error); } @Override public void onClose(int exitCode) { System.out.println("Exit code: " + exitCode); } }); // Interactive shell with TTY final ExecOptions interactive = ExecOptions.interactive(new String[]{"/bin/bash"}); client.pods().exec("default", "my-pod", interactive, execWatch); // Execute in specific container (multi-container pods) client.pods().exec("default", "my-pod", "nginx", new String[]{"nginx", "-t"}, execWatch); // Advanced options final ExecOptions advanced = ExecOptions.builder() .command(new String[]{"python", "script.py"}) .stdin(true) .stdout(true) .stderr(true) .tty(false) .container("app") .build(); client.pods().exec("default", "my-pod", advanced, execWatch);
Features:
- Full WebSocket-based bidirectional streaming (STDIN/STDOUT/STDERR)
- Channel multiplexing via Kubernetes exec protocol (v4.channel.k8s.io)
- Exit code extraction and error handling
- Interactive TTY support
- Multi-container pod support
- Complete unit test coverage
Technical Details:
- Uses OkHttp WebSocket client with proper connection management
- Implements Kubernetes channel multiplexing protocol
- Automatic exit code parsing from ERROR channel
- Resource cleanup via AutoCloseable pattern
Port Forwarding
Forward network traffic from local ports to pod ports via WebSocket:
import io.elev8.core.portforward.PortForwardOptions; import io.elev8.core.portforward.PortForwardWatch; // Forward a single port client.pods().portForward("default", "my-pod", 8080, new PortForwardWatch() { @Override public void onData(int port, byte[] data) { // Handle data received from the pod System.out.println("Received " + data.length + " bytes from port " + port); } @Override public void onError(int port, String error) { System.err.println("Error on port " + port + ": " + error); } @Override public void onClose() { System.out.println("Port forward session closed"); } }); // Forward multiple ports final PortForwardOptions options = PortForwardOptions.of(8080, 9090, 3000); client.pods().portForward("default", "my-pod", options, new PortForwardWatch() { @Override public void onData(int port, byte[] data) { System.out.println("Port " + port + ": " + new String(data)); } // Write data to a forwarded port public void sendRequest(int port) { final byte[] request = "GET / HTTP/1.1\r\n\r\n".getBytes(); writeData(port, request); } }); // Forward ports in specific container (multi-container pods) final PortForwardOptions containerOptions = PortForwardOptions.withContainer(new int[]{8080}, "nginx"); client.pods().portForward("default", "my-pod", containerOptions, portForwardWatch);
Features:
- WebSocket-based bidirectional streaming for network traffic
- Channel multiplexing via Kubernetes port-forward protocol (v4.channel.k8s.io)
- Support for multiple ports in single connection
- Multi-container pod support
- Binary data handling for any protocol (HTTP, TCP, etc.)
- Complete unit test coverage
Technical Details:
- Uses OkHttp WebSocket client with proper connection management
- Implements Kubernetes channel multiplexing protocol
- Channel pairs per port (even=data, odd=error)
- Resource cleanup via AutoCloseable pattern
Authentication Modes Comparison
| Feature | IAM Auth | OIDC/IRSA | Access Entries | Token |
| Zero Config | ✅ | ✅ |
Recommendation: Use Access Entries for new clusters, IAM Auth for existing clusters.
Project Structure
elev8/
├── elev8-core/ # Core abstractions and client framework
├── elev8-auth-iam/ # AWS IAM authentication
├── elev8-auth-oidc/ # OIDC/IRSA authentication
├── elev8-auth-accessentries/# EKS Access Entries API
├── elev8-auth-token/ # Service account tokens
├── elev8-resources/ # Kubernetes resource plugins
├── elev8-eks/ # EKS-specific features and client
└── examples/ # Usage examples
Building from Source
# Clone the repository git clone https://github.com/Ignoramuss/elev8.git cd elev8 # Build all modules mvn clean install # Run tests mvn test # Build without tests mvn clean install -DskipTests # Build a specific module cd elev8-core mvn clean install
Logging
Elev8 uses SLF4J as a logging facade, following best practices for open source Java libraries. This means you have complete control over the logging implementation.
Adding a Logging Implementation
Elev8 only depends on slf4j-api. You must add your own SLF4J implementation:
Option 1: Logback (Recommended)
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.17</version> </dependency>
Option 2: Log4j 2
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> <version>2.24.3</version> </dependency>
Option 3: Simple Logger (for testing)
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.16</version> </dependency>
Configuring Logback
Create src/main/resources/logback.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- Set Elev8 logging level --> <logger name="io.elev8" level="INFO"/> <!-- Set AWS SDK logging level (reduce noise) --> <logger name="software.amazon.awssdk" level="WARN"/> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>
Log Levels
- TRACE: Very detailed debugging (token generation details)
- DEBUG: IAM authentication flow, HTTP requests
- INFO: Client initialization, resource operations
- WARN: Deprecated features, potential issues
- ERROR: Authentication failures, API errors
Examples
See the examples directory for comprehensive examples:
- IAM Authentication from EC2
- IAM Authentication from Lambda
- OIDC/IRSA Authentication
- EKS Access Entries Management
- Multi-Cluster Setup
- Custom Resource Definitions
Migration from fabric8
Elev8 provides a familiar API for fabric8 users:
// fabric8 Config config = new ConfigBuilder() .withMasterUrl(url) .withOauthToken(token) .build(); KubernetesClient client = new KubernetesClientBuilder() .withConfig(config) .build(); // Elev8 final EksClient client = EksClient.builder() .clusterName("my-cluster") .region("us-east-1") // No manual token management! .build();
kubectl Command Equivalents
Elev8 provides type-safe Java alternatives to common kubectl commands:
Pod Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get pods -n default |
client.pods().list("default") |
kubectl get pods --all-namespaces |
client.pods().listAllNamespaces() |
kubectl get pod my-pod -n default |
client.pods().get("default", "my-pod") |
kubectl get pod my-pod -n default -o json |
final Pod pod = client.pods().get("default", "my-pod"); String json = pod.toJson(); |
kubectl create -f pod.yaml |
Pod pod = Pod.builder()...build(); client.pods().create(pod); |
kubectl delete pod my-pod -n default |
client.pods().delete("default", "my-pod") |
kubectl describe pod my-pod |
final Pod pod = client.pods().get("default", "my-pod"); // Access pod.getStatus(), pod.getSpec() |
Service Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get services -n default |
client.services().list("default") |
kubectl get svc my-service -n default |
client.services().get("default", "my-service") |
kubectl create -f service.yaml |
Service svc = Service.builder()...build(); client.services().create(svc); |
kubectl delete service my-service |
client.services().delete("default", "my-service") |
kubectl expose deployment my-deploy --port=80 |
Service svc = Service.builder() .name("my-deploy") .spec(ServiceSpec.builder() .addSelector("app", "my-deploy") .addPort(80, 8080) .build()) .build(); client.services().create(svc); |
Deployment Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get deployments -n default |
client.deployments().list("default") |
kubectl get deployment my-deploy -n default |
client.deployments().get("default", "my-deploy") |
kubectl create -f deployment.yaml |
Deployment deploy = Deployment.builder()...build(); client.deployments().create(deploy); |
kubectl apply -f deployment.yaml |
Deployment deploy = ...; // modified deployment client.deployments().update(deploy); |
kubectl delete deployment my-deploy |
client.deployments().delete("default", "my-deploy") |
kubectl scale deployment my-deploy --replicas=5 |
final Deployment d = client.deployments().get("default", "my-deploy"); d.getSpec().setReplicas(5); client.deployments().update(d); |
kubectl rollout restart deployment/my-deploy |
final Deployment d = client.deployments().get("default", "my-deploy"); // Add/update annotation to trigger restart d.getMetadata().getAnnotations().put("kubectl.kubernetes.io/restartedAt", Instant.now().toString()); client.deployments().update(d); |
ConfigMap Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get configmaps -n default |
client.configMaps().list("default") |
kubectl get configmap my-config -n default |
client.configMaps().get("default", "my-config") |
kubectl create configmap my-config --from-literal=key=value |
ConfigMap cm = ConfigMap.builder() .name("my-config") .namespace("default") .addData("key", "value") .build(); client.configMaps().create(cm); |
kubectl delete configmap my-config |
client.configMaps().delete("default", "my-config") |
kubectl create configmap app-config --from-literal=env=prod |
ConfigMap cm = ConfigMap.builder() .name("app-config") .namespace("default") .addData("env", "prod") .build(); client.configMaps().create(cm); |
Secret Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get secrets -n default |
client.secrets().list("default") |
kubectl get secret my-secret -n default |
client.secrets().get("default", "my-secret") |
kubectl create secret generic my-secret --from-literal=password=abc123 |
Secret s = Secret.builder() .name("my-secret") .namespace("default") .addStringData("password", "abc123") .build(); client.secrets().create(s); |
kubectl create secret tls tls-secret --cert=path/to/cert --key=path/to/key |
Secret s = Secret.builder() .name("tls-secret") .namespace("default") .tls("base64-cert", "base64-key") .build(); client.secrets().create(s); |
kubectl delete secret my-secret |
client.secrets().delete("default", "my-secret") |
DaemonSet Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get daemonsets -n kube-system |
client.daemonSets().list("kube-system") |
kubectl get daemonset fluentd -n kube-system |
client.daemonSets().get("kube-system", "fluentd") |
kubectl create -f daemonset.yaml |
DaemonSet ds = DaemonSet.builder()...build(); client.daemonSets().create(ds); |
kubectl delete daemonset fluentd -n kube-system |
client.daemonSets().delete("kube-system", "fluentd") |
kubectl rollout status daemonset/fluentd -n kube-system |
final DaemonSet ds = client.daemonSets().get("kube-system", "fluentd"); // Check ds.getStatus().getNumberReady() and ds.getStatus().getDesiredNumberScheduled() |
Job Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get jobs -n default |
client.jobs().list("default") |
kubectl get job my-job -n default |
client.jobs().get("default", "my-job") |
kubectl create -f job.yaml |
Job job = Job.builder()...build(); client.jobs().create(job); |
kubectl delete job my-job -n default |
client.jobs().delete("default", "my-job") |
kubectl logs job/my-job -n default |
final Job job = client.jobs().get("default", "my-job"); // Get pod logs using job.getStatus() to find pod names |
kubectl wait --for=condition=complete job/my-job |
final Job job = client.jobs().get("default", "my-job"); // Poll job.getStatus().getSucceeded() until equals completions |
StatefulSet Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get statefulsets -n default |
client.statefulSets().list("default") |
kubectl get statefulset web -n default |
client.statefulSets().get("default", "web") |
kubectl create -f statefulset.yaml |
StatefulSet sts = StatefulSet.builder()...build(); client.statefulSets().create(sts); |
kubectl delete statefulset web -n default |
client.statefulSets().delete("default", "web") |
kubectl scale statefulset web --replicas=5 |
final StatefulSet sts = client.statefulSets().get("default", "web"); sts.getSpec().setReplicas(5); client.statefulSets().update(sts); |
kubectl rollout status statefulset/web -n default |
final StatefulSet sts = client.statefulSets().get("default", "web"); // Check sts.getStatus().getReadyReplicas() and sts.getStatus().getReplicas() |
ReplicaSet Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get replicasets -n default |
client.replicaSets().list("default") |
kubectl get replicaset nginx-replicaset -n default |
client.replicaSets().get("default", "nginx-replicaset") |
kubectl create -f replicaset.yaml |
ReplicaSet rs = ReplicaSet.builder()...build(); client.replicaSets().create(rs); |
kubectl delete replicaset nginx-replicaset -n default |
client.replicaSets().delete("default", "nginx-replicaset") |
kubectl scale replicaset nginx-replicaset --replicas=5 |
final ReplicaSet rs = client.replicaSets().get("default", "nginx-replicaset"); rs.getSpec().setReplicas(5); client.replicaSets().update(rs); |
kubectl get replicaset nginx-replicaset -o json |
final ReplicaSet rs = client.replicaSets().get("default", "nginx-replicaset"); String json = rs.toJson(); |
Ingress Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get ingress -n default |
client.ingresses().list("default") |
kubectl get ingress example-ingress -n default |
client.ingresses().get("default", "example-ingress") |
kubectl create -f ingress.yaml |
Ingress ing = Ingress.builder()...build(); client.ingresses().create(ing); |
kubectl delete ingress example-ingress -n default |
client.ingresses().delete("default", "example-ingress") |
kubectl describe ingress example-ingress |
final Ingress ing = client.ingresses().get("default", "example-ingress"); // Check ing.getStatus().getLoadBalancer() |
kubectl get ingress example-ingress -o json |
final Ingress ing = client.ingresses().get("default", "example-ingress"); String json = ing.toJson(); |
CronJob Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get cronjobs -n default |
client.cronJobs().list("default") |
kubectl get cronjob hello -n default |
client.cronJobs().get("default", "hello") |
kubectl create -f cronjob.yaml |
CronJob cj = CronJob.builder()...build(); client.cronJobs().create(cj); |
kubectl delete cronjob hello -n default |
client.cronJobs().delete("default", "hello") |
kubectl patch cronjob hello -p '{"spec":{"suspend":true}}' |
final CronJob cj = client.cronJobs().get("default", "hello"); cj.getSpec().setSuspend(true); client.cronJobs().update(cj); |
kubectl get cronjob hello -o json |
final CronJob cj = client.cronJobs().get("default", "hello"); String json = cj.toJson(); |
Namespace Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get namespaces |
client.namespaces().listAllNamespaces() |
kubectl get namespace production |
client.namespaces().get("production") |
kubectl create namespace development |
Namespace ns = Namespace.builder() .name("development") .build(); client.namespaces().create(ns); |
kubectl create namespace prod --labels=env=production |
Namespace ns = Namespace.builder() .name("prod") .label("env", "production") .build(); client.namespaces().create(ns); |
kubectl delete namespace development |
client.namespaces().delete("development") |
kubectl get namespace production -o json |
final Namespace ns = client.namespaces().get("production"); String json = ns.toJson(); |
ServiceAccount Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get serviceaccounts -n default |
client.serviceAccounts().list("default") |
kubectl get serviceaccount my-service-account -n default |
client.serviceAccounts().get("default", "my-service-account") |
kubectl create serviceaccount my-service-account -n default |
ServiceAccount sa = ServiceAccount.builder() .name("my-service-account") .namespace("default") .build(); client.serviceAccounts().create(sa); |
kubectl delete serviceaccount my-service-account -n default |
client.serviceAccounts().delete("default", "my-service-account") |
kubectl describe serviceaccount my-service-account -n default |
final ServiceAccount sa = client.serviceAccounts().get("default", "my-service-account"); // Check sa.getStatus().getSecrets() |
kubectl get serviceaccount my-service-account -o json |
final ServiceAccount sa = client.serviceAccounts().get("default", "my-service-account"); String json = sa.toJson(); |
Role Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get roles -n default |
client.roles().list("default") |
kubectl get role pod-reader -n default |
client.roles().get("default", "pod-reader") |
kubectl create role pod-reader --verb=get --verb=list --resource=pods -n default |
Role role = Role.builder() .name("pod-reader") .namespace("default") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .verb("get") .verb("list") .build()) .build()) .build(); client.roles().create(role); |
kubectl delete role pod-reader -n default |
client.roles().delete("default", "pod-reader") |
kubectl describe role pod-reader -n default |
final Role role = client.roles().get("default", "pod-reader"); // Check role.getSpec().getRules() |
kubectl get role pod-reader -o json |
final Role role = client.roles().get("default", "pod-reader"); String json = role.toJson(); |
RoleBinding Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get rolebindings -n default |
client.roleBindings().list("default") |
kubectl get rolebinding read-pods -n default |
client.roleBindings().get("default", "read-pods") |
kubectl delete rolebinding read-pods -n default |
client.roleBindings().delete("default", "read-pods") |
kubectl describe rolebinding read-pods -n default |
final RoleBinding rb = client.roleBindings().get("default", "read-pods"); // Check rb.getSpec().getSubjects() and rb.getSpec().getRoleRef() |
kubectl get rolebinding read-pods -o json |
final RoleBinding rb = client.roleBindings().get("default", "read-pods"); String json = rb.toJson(); |
ClusterRole Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get clusterroles |
client.clusterRoles().list() |
kubectl get clusterrole pod-reader |
client.clusterRoles().get("pod-reader") |
kubectl create clusterrole pod-reader --verb=get --verb=list --resource=pods |
ClusterRole role = ClusterRole.builder() .name("pod-reader") .spec(RoleSpec.builder() .rule(PolicyRule.builder() .apiGroup("") .resource("pods") .verb("get") .verb("list") .build()) .build()) .build(); client.clusterRoles().create(role); |
kubectl delete clusterrole pod-reader |
client.clusterRoles().delete("pod-reader") |
kubectl describe clusterrole pod-reader |
final ClusterRole role = client.clusterRoles().get("pod-reader"); // Check role.getSpec().getRules() |
kubectl get clusterrole pod-reader -o json |
final ClusterRole role = client.clusterRoles().get("pod-reader"); String json = role.toJson(); |
ClusterRoleBinding Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get clusterrolebindings |
client.clusterRoleBindings().list() |
kubectl get clusterrolebinding cluster-admin-binding |
client.clusterRoleBindings().get("cluster-admin-binding") |
kubectl delete clusterrolebinding cluster-admin-binding |
client.clusterRoleBindings().delete("cluster-admin-binding") |
kubectl describe clusterrolebinding cluster-admin-binding |
final ClusterRoleBinding rb = client.clusterRoleBindings().get("cluster-admin-binding"); // Check rb.getSpec().getSubjects() and rb.getSpec().getRoleRef() |
kubectl get clusterrolebinding cluster-admin-binding -o json |
final ClusterRoleBinding rb = client.clusterRoleBindings().get("cluster-admin-binding"); String json = rb.toJson(); |
NetworkPolicy Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get networkpolicies -n default |
client.networkPolicies().list("default") |
kubectl get networkpolicy allow-db-access -n default |
client.networkPolicies().get("default", "allow-db-access") |
kubectl delete networkpolicy allow-db-access -n default |
client.networkPolicies().delete("default", "allow-db-access") |
kubectl describe networkpolicy allow-db-access -n default |
final NetworkPolicy np = client.networkPolicies().get("default", "allow-db-access"); // Check np.getSpec().getIngress() and np.getSpec().getEgress() |
kubectl get networkpolicy allow-db-access -o json |
final NetworkPolicy np = client.networkPolicies().get("default", "allow-db-access"); String json = np.toJson(); |
HorizontalPodAutoscaler Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get hpa -n default |
client.horizontalPodAutoscalers().list("default") |
kubectl get hpa php-apache -n default |
client.horizontalPodAutoscalers().get("default", "php-apache") |
kubectl delete hpa php-apache -n default |
client.horizontalPodAutoscalers().delete("default", "php-apache") |
kubectl describe hpa php-apache -n default |
final HorizontalPodAutoscaler hpa = client.horizontalPodAutoscalers().get("default", "php-apache"); // Check hpa.getStatus().getCurrentReplicas(), hpa.getStatus().getDesiredReplicas() |
kubectl get hpa php-apache -o json |
final HorizontalPodAutoscaler hpa = client.horizontalPodAutoscalers().get("default", "php-apache"); String json = hpa.toJson(); |
VerticalPodAutoscaler Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get vpa -n default |
client.verticalPodAutoscalers().list("default") |
kubectl get vpa my-app-vpa -n default |
client.verticalPodAutoscalers().get("default", "my-app-vpa") |
kubectl apply -f vpa.yaml |
VerticalPodAutoscaler vpa = VerticalPodAutoscaler.builder() .name("my-app-vpa") .namespace("default") .spec(VerticalPodAutoscalerSpec.builder() .targetRef(CrossVersionObjectReference.builder() .apiVersion("apps/v1") .kind("Deployment") .name("my-app") .build()) .updatePolicy(VPAUpdatePolicy.builder() .updateMode("Off") .build()) .build()) .build(); client.verticalPodAutoscalers().create(vpa); |
kubectl delete vpa my-app-vpa -n default |
client.verticalPodAutoscalers().delete("default", "my-app-vpa") |
kubectl describe vpa my-app-vpa -n default |
final VerticalPodAutoscaler vpa = client.verticalPodAutoscalers().get("default", "my-app-vpa"); // Check vpa.getStatus().getRecommendation() |
kubectl get vpa my-app-vpa -o json |
final VerticalPodAutoscaler vpa = client.verticalPodAutoscalers().get("default", "my-app-vpa"); String json = vpa.toJson(); |
ResourceQuota Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get resourcequota -n my-ns |
client.resourceQuotas().list("my-ns") |
kubectl get resourcequota compute-quota -n my-ns |
client.resourceQuotas().get("my-ns", "compute-quota") |
kubectl create -f quota.yaml |
ResourceQuota quota = ResourceQuota.builder() .name("compute-quota") .namespace("my-ns") .spec(ResourceQuotaSpec.builder() .hardLimit("requests.cpu", "10") .hardLimit("requests.memory", "20Gi") .hardLimit("pods", "50") .build()) .build(); client.resourceQuotas().create(quota); |
kubectl delete resourcequota compute-quota -n my-ns |
client.resourceQuotas().delete("my-ns", "compute-quota") |
kubectl describe resourcequota compute-quota -n my-ns |
final ResourceQuota quota = client.resourceQuotas().get("my-ns", "compute-quota"); // Check quota.getStatus().getUsed() vs quota.getStatus().getHard() |
kubectl get resourcequota compute-quota -o json |
final ResourceQuota quota = client.resourceQuotas().get("my-ns", "compute-quota"); String json = quota.toJson(); |
PersistentVolume Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get pv |
client.persistentVolumes().list() |
kubectl get pv local-pv |
client.persistentVolumes().get("local-pv") |
kubectl create -f persistentvolume.yaml |
PersistentVolume pv = PersistentVolume.builder()...build(); client.persistentVolumes().create(pv); |
kubectl delete pv local-pv |
client.persistentVolumes().delete("local-pv") |
kubectl describe pv local-pv |
final PersistentVolume pv = client.persistentVolumes().get("local-pv"); // Check pv.getSpec() and pv.getStatus() |
kubectl get pv local-pv -o json |
final PersistentVolume pv = client.persistentVolumes().get("local-pv"); String json = pv.toJson(); |
PersistentVolumeClaim Operations
| kubectl Command | Elev8 Equivalent |
|---|---|
kubectl get pvc -n default |
client.persistentVolumeClaims().list("default") |
kubectl get pvc my-pvc -n default |
client.persistentVolumeClaims().get("default", "my-pvc") |
kubectl create -f persistentvolumeclaim.yaml |
PersistentVolumeClaim pvc = PersistentVolumeClaim.builder()...build(); client.persistentVolumeClaims().create(pvc); |
kubectl delete pvc my-pvc -n default |
client.persistentVolumeClaims().delete("default", "my-pvc") |
kubectl describe pvc my-pvc -n default |
final PersistentVolumeClaim pvc = client.persistentVolumeClaims().get("default", "my-pvc"); // Check pvc.getSpec() and pvc.getStatus() |
kubectl get pvc my-pvc -o json |
final PersistentVolumeClaim pvc = client.persistentVolumeClaims().get("default", "my-pvc"); String json = pvc.toJson(); |
Complete Example: Creating a Deployment
kubectl:
kubectl create deployment nginx --image=nginx:1.21 --replicas=3 -n default kubectl expose deployment nginx --port=80 --target-port=80
Elev8:
// Create deployment final Deployment deployment = Deployment.builder() .name("nginx") .namespace("default") .spec(DeploymentSpec.builder() .replicas(3) .addSelector("app", "nginx") .template(PodTemplateSpec.builder() .label("app", "nginx") .spec(PodSpec.builder() .addContainer(Container.builder() .name("nginx") .image("nginx:1.21") .addPort(80) .build()) .build()) .build()) .build()) .build(); client.deployments().create(deployment); // Create service final Service service = Service.builder() .name("nginx") .namespace("default") .spec(ServiceSpec.builder() .addSelector("app", "nginx") .addPort(80, 80) .type("ClusterIP") .build()) .build(); client.services().create(service);
Key Advantages Over kubectl
- Type Safety: Compile-time validation of all fields
- IDE Support: Auto-completion and inline documentation
- Programmatic Control: Full control flow and error handling
- Integration: Seamlessly integrates with Java applications
- No External Dependencies: No need to install kubectl
- Consistent API: Same pattern for all resource types
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for details.
License
Apache License 2.0 - see LICENSE for details.
Support
Roadmap
See ROADMAP.md for the complete development roadmap including completed features and future plans.