Java client for Kubernetes and OpenShift providing access to full REST APIs via a fluent DSL
—
This document covers OpenShift project operations, including project creation with role bindings, project requests, and project management.
import io.fabric8.openshift.client.OpenShiftClient;
import io.fabric8.openshift.client.dsl.ProjectOperation;
import io.fabric8.openshift.client.dsl.ProjectRequestOperation;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.NamespaceList;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;OpenShift projects extend Kubernetes namespaces with additional OpenShift-specific functionality, including role-based access control and resource quotas.
// List all projects (equivalent to namespaces with OpenShift metadata)
List<Namespace> projects = client.projects().list().getItems();
// Get a specific project
Namespace project = client.projects()
.withName("my-project")
.get();
// Check if project exists
boolean exists = client.projects()
.withName("my-project")
.get() != null;// Create a simple project (namespace)
Namespace simpleProject = client.projects()
.create(new NamespaceBuilder()
.withNewMetadata()
.withName("simple-project")
.addToLabels("type", "development")
.endMetadata()
.build());
// Create project with display name and description
Namespace projectWithMetadata = client.projects()
.create(new NamespaceBuilder()
.withNewMetadata()
.withName("my-application")
.addToAnnotations("openshift.io/display-name", "My Application")
.addToAnnotations("openshift.io/description", "Development project for my application")
.addToLabels("environment", "development")
.endMetadata()
.build());// Create project with automatic role bindings
Namespace projectWithRoles = client.projects()
.createProjectAndRoleBindings(
"team-project", // project name
"Team Development Project", // description
"Team Project", // display name
"developer-user" // admin user
);
// Create project with multiple administrators
Namespace multiAdminProject = client.projects()
.createProjectAndRoleBindings(
"multi-admin-project",
"Multi-administrator project",
"Multi Admin Project",
"admin-user1,admin-user2" // comma-separated admin users
);// Delete a project (this will delete the namespace and all resources)
Boolean deleted = client.projects()
.withName("obsolete-project")
.delete();
// Delete project with grace period
Boolean gracefullyDeleted = client.projects()
.withName("project-to-delete")
.withGracePeriod(30) // 30 seconds
.delete();
// Force delete project (immediate deletion)
Boolean forceDeleted = client.projects()
.withName("stuck-project")
.withGracePeriod(0)
.delete();ProjectRequest is used to request the creation of new projects, typically by regular users who don't have cluster-admin privileges.
// Create a project request (for non-admin users)
ProjectRequest projectRequest = new ProjectRequestBuilder()
.withNewMetadata()
.withName("requested-project")
.endMetadata()
.withDisplayName("Requested Project")
.withDescription("Project requested by developer")
.build();
// Submit project request
ProjectRequest submittedRequest = client.projectrequests()
.create(projectRequest);
// The project request will create a project if the user has permission
Namespace createdProject = client.projects()
.withName("requested-project")
.get();// Project request with detailed metadata
ProjectRequest detailedRequest = new ProjectRequestBuilder()
.withNewMetadata()
.withName("detailed-project")
.addToAnnotations("openshift.io/requester", "john.doe")
.endMetadata()
.withDisplayName("Detailed Development Project")
.withDescription("Comprehensive project for application development")
.build();
ProjectRequest created = client.projectrequests()
.create(detailedRequest);// Watch project changes
client.projects()
.watch(new Watcher<Namespace>() {
@Override
public void eventReceived(Action action, Namespace project) {
System.out.println("Project " + action + ": " +
project.getMetadata().getName());
String displayName = project.getMetadata().getAnnotations()
.get("openshift.io/display-name");
if (displayName != null) {
System.out.println(" Display Name: " + displayName);
}
}
@Override
public void onClose(WatcherException cause) {
System.out.println("Project watch closed: " + cause.getMessage());
}
});
// Get project status and phase
Namespace project = client.projects()
.withName("my-project")
.get();
String phase = project.getStatus().getPhase(); // Active, Terminating
System.out.println("Project phase: " + phase);// Update project annotations and labels
Namespace updatedProject = client.projects()
.withName("my-project")
.edit(project -> new NamespaceBuilder(project)
.editMetadata()
.addToAnnotations("openshift.io/display-name", "Updated Display Name")
.addToAnnotations("openshift.io/description", "Updated description")
.addToLabels("team", "backend")
.addToLabels("environment", "production")
.endMetadata()
.build());
// Remove annotations
Namespace cleanedProject = client.projects()
.withName("my-project")
.edit(project -> new NamespaceBuilder(project)
.editMetadata()
.removeFromAnnotations("obsolete-annotation")
.endMetadata()
.build());// Get all resources in a project
client.inNamespace("my-project").pods().list();
client.inNamespace("my-project").services().list();
client.inNamespace("my-project").deploymentConfigs().list();
client.inNamespace("my-project").routes().list();
// Count resources in project
int podCount = client.inNamespace("my-project").pods().list().getItems().size();
int serviceCount = client.inNamespace("my-project").services().list().getItems().size();
System.out.println("Project my-project has " + podCount + " pods and " +
serviceCount + " services");// Initialize project with template
public void initializeProjectFromTemplate(String projectName, String templateName) {
// 1. Create project
Namespace project = client.projects()
.createProjectAndRoleBindings(
projectName,
"Project created from template " + templateName,
projectName,
client.currentUser().getMetadata().getName()
);
// 2. Apply template
Template processed = client.templates()
.inNamespace("openshift") // Common template namespace
.withName(templateName)
.withParameter("PROJECT_NAME", projectName)
.process();
// 3. Create objects in the new project
for (HasMetadata object : processed.getObjects()) {
object.getMetadata().setNamespace(projectName);
client.resource(object).createOrReplace();
}
}// Add user to project with admin role
RoleBinding adminBinding = new RoleBindingBuilder()
.withNewMetadata()
.withName("admin-binding")
.withNamespace("my-project")
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("admin")
.endRoleRef()
.addNewSubject()
.withKind("User")
.withName("new-admin-user")
.withApiGroup("rbac.authorization.k8s.io")
.endSubject()
.build();
client.rbac().roleBindings()
.inNamespace("my-project")
.create(adminBinding);
// Add group to project with view role
RoleBinding viewBinding = new RoleBindingBuilder()
.withNewMetadata()
.withName("viewers-binding")
.withNamespace("my-project")
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("view")
.endRoleRef()
.addNewSubject()
.withKind("Group")
.withName("developers")
.withApiGroup("rbac.authorization.k8s.io")
.endSubject()
.build();
client.rbac().roleBindings()
.inNamespace("my-project")
.create(viewBinding);// Set resource quota for project
ResourceQuota quota = new ResourceQuotaBuilder()
.withNewMetadata()
.withName("project-quota")
.withNamespace("my-project")
.endMetadata()
.withNewSpec()
.addToHard("requests.cpu", new Quantity("2"))
.addToHard("requests.memory", new Quantity("4Gi"))
.addToHard("limits.cpu", new Quantity("4"))
.addToHard("limits.memory", new Quantity("8Gi"))
.addToHard("persistentvolumeclaims", new Quantity("10"))
.addToHard("pods", new Quantity("20"))
.addToHard("services", new Quantity("10"))
.endSpec()
.build();
client.resourceQuotas()
.inNamespace("my-project")
.create(quota);
// Check quota usage
ResourceQuota currentQuota = client.resourceQuotas()
.inNamespace("my-project")
.withName("project-quota")
.get();
Map<String, Quantity> used = currentQuota.getStatus().getUsed();
Map<String, Quantity> hard = currentQuota.getStatus().getHard();
System.out.println("CPU usage: " + used.get("requests.cpu") +
" / " + hard.get("requests.cpu"));// Create network policy to isolate project
NetworkPolicy isolationPolicy = new NetworkPolicyBuilder()
.withNewMetadata()
.withName("deny-all")
.withNamespace("my-project")
.endMetadata()
.withNewSpec()
.withNewPodSelector() // Selects all pods
.endPodSelector()
.withPolicyTypes("Ingress", "Egress")
// Empty ingress/egress rules = deny all
.endSpec()
.build();
client.network().v1().networkPolicies()
.inNamespace("my-project")
.create(isolationPolicy);
// Allow ingress from specific project
NetworkPolicy allowFromProject = new NetworkPolicyBuilder()
.withNewMetadata()
.withName("allow-from-frontend")
.withNamespace("my-project")
.endMetadata()
.withNewSpec()
.withNewPodSelector()
.addToMatchLabels("app", "backend")
.endPodSelector()
.withPolicyTypes("Ingress")
.addNewIngress()
.addNewFrom()
.withNewNamespaceSelector()
.addToMatchLabels("name", "frontend-project")
.endNamespaceSelector()
.endFrom()
.endIngress()
.endSpec()
.build();
client.network().v1().networkPolicies()
.inNamespace("my-project")
.create(allowFromProject);import io.fabric8.openshift.client.OpenShiftClient;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.rbac.*;
public class ProjectManager {
private final OpenShiftClient client;
public ProjectManager(OpenShiftClient client) {
this.client = client;
}
public void setupCompleteProject(String projectName, String displayName,
String description, String adminUser,
List<String> developers, List<String> viewers) {
// 1. Create project with initial admin
System.out.println("Creating project: " + projectName);
Namespace project = client.projects()
.createProjectAndRoleBindings(projectName, description, displayName, adminUser);
// 2. Set up additional role bindings
setupProjectRoles(projectName, developers, viewers);
// 3. Create resource quota
setupResourceQuota(projectName);
// 4. Create network policies
setupNetworkPolicies(projectName);
// 5. Create default pull secret
setupPullSecrets(projectName);
System.out.println("Project setup completed: " + projectName);
}
private void setupProjectRoles(String projectName, List<String> developers,
List<String> viewers) {
// Add developers with edit role
if (!developers.isEmpty()) {
RoleBinding devBinding = new RoleBindingBuilder()
.withNewMetadata()
.withName("developers")
.withNamespace(projectName)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("edit")
.endRoleRef()
.build();
for (String dev : developers) {
devBinding.getSubjects().add(new SubjectBuilder()
.withKind("User")
.withName(dev)
.withApiGroup("rbac.authorization.k8s.io")
.build());
}
client.rbac().roleBindings()
.inNamespace(projectName)
.create(devBinding);
}
// Add viewers with view role
if (!viewers.isEmpty()) {
RoleBinding viewBinding = new RoleBindingBuilder()
.withNewMetadata()
.withName("viewers")
.withNamespace(projectName)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("view")
.endRoleRef()
.build();
for (String viewer : viewers) {
viewBinding.getSubjects().add(new SubjectBuilder()
.withKind("User")
.withName(viewer)
.withApiGroup("rbac.authorization.k8s.io")
.build());
}
client.rbac().roleBindings()
.inNamespace(projectName)
.create(viewBinding);
}
}
private void setupResourceQuota(String projectName) {
ResourceQuota quota = new ResourceQuotaBuilder()
.withNewMetadata()
.withName("compute-quota")
.withNamespace(projectName)
.endMetadata()
.withNewSpec()
.addToHard("requests.cpu", new Quantity("4"))
.addToHard("requests.memory", new Quantity("8Gi"))
.addToHard("limits.cpu", new Quantity("8"))
.addToHard("limits.memory", new Quantity("16Gi"))
.addToHard("persistentvolumeclaims", new Quantity("20"))
.addToHard("pods", new Quantity("50"))
.addToHard("services", new Quantity("20"))
.addToHard("secrets", new Quantity("50"))
.addToHard("configmaps", new Quantity("50"))
.endSpec()
.build();
client.resourceQuotas()
.inNamespace(projectName)
.create(quota);
}
private void setupNetworkPolicies(String projectName) {
// Allow ingress from OpenShift system namespaces
NetworkPolicy allowSystem = new NetworkPolicyBuilder()
.withNewMetadata()
.withName("allow-from-openshift")
.withNamespace(projectName)
.endMetadata()
.withNewSpec()
.withNewPodSelector() // All pods
.endPodSelector()
.withPolicyTypes("Ingress")
.addNewIngress()
.addNewFrom()
.withNewNamespaceSelector()
.addToMatchLabels("name", "openshift-ingress")
.endNamespaceSelector()
.endFrom()
.endIngress()
.addNewIngress()
.addNewFrom()
.withNewNamespaceSelector()
.addToMatchLabels("name", "openshift-monitoring")
.endNamespaceSelector()
.endFrom()
.endIngress()
.endSpec()
.build();
client.network().v1().networkPolicies()
.inNamespace(projectName)
.create(allowSystem);
}
private void setupPullSecrets(String projectName) {
// Link default pull secret to default service account
ServiceAccount defaultSA = client.serviceAccounts()
.inNamespace(projectName)
.withName("default")
.get();
if (defaultSA != null) {
client.serviceAccounts()
.inNamespace(projectName)
.withName("default")
.edit(sa -> new ServiceAccountBuilder(sa)
.addNewImagePullSecret()
.withName("default-dockercfg-secret")
.endImagePullSecret()
.build());
}
}
public void cleanupProject(String projectName) {
System.out.println("Cleaning up project: " + projectName);
// Get project first to ensure it exists
Namespace project = client.projects().withName(projectName).get();
if (project == null) {
System.out.println("Project not found: " + projectName);
return;
}
// Delete project (this cascades to all resources)
Boolean deleted = client.projects()
.withName(projectName)
.delete();
if (deleted) {
// Wait for project deletion
client.projects()
.withName(projectName)
.waitUntilCondition(Objects::isNull, 5, TimeUnit.MINUTES);
System.out.println("Project deleted: " + projectName);
} else {
System.err.println("Failed to delete project: " + projectName);
}
}
}public interface ProjectOperation extends NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> {
/**
* Create a new project with role bindings for the specified user.
*
* @param name project name
* @param description project description
* @param displayName project display name
* @param adminUser user to grant admin access
* @return created Namespace (project)
*/
Namespace createProjectAndRoleBindings(String name, String description,
String displayName, String adminUser);
}public interface ProjectRequestOperation extends
InOutCreateable<ProjectRequest, ProjectRequest> {
// Inherits create methods for ProjectRequest objects
}public class ProjectRequest implements HasMetadata {
public ObjectMeta getMetadata();
public String getDisplayName();
public String getDescription();
}// Projects are represented as Kubernetes Namespaces with additional annotations
public class Namespace implements HasMetadata {
public ObjectMeta getMetadata();
public NamespaceSpec getSpec();
public NamespaceStatus getStatus();
}
public class NamespaceSpec {
public List<String> getFinalizers();
}
public class NamespaceStatus {
public String getPhase(); // Active, Terminating
public List<NamespaceCondition> getConditions();
}Install with Tessl CLI
npx tessl i tessl/maven-io-fabric8--kubernetes-client-project