Hierarchical Namespaces (HNC) extend Kubernetes namespaces to support parent-child relationships, enabling better organization and inheritance of resources across multi-tenant environments.
HNC Architecture Overview
Key Concepts:
- Hierarchical Namespace Controller (HNC): Kubernetes operator managing namespace hierarchies
- Parent-Child Relationships: Namespaces can have parent-child relationships
- Resource Propagation: Policies, RBAC, and secrets can be inherited
- Multi-tenancy: Support for complex organizational structures
Java Application → HNC → Kubernetes
- Applications interact with hierarchical namespaces
- HNC manages propagation and inheritance
- Kubernetes provides the underlying namespace mechanism
Prerequisites and Dependencies
Maven Dependencies
<properties> <spring-boot.version>3.1.0</spring-boot.version> <kubernetes-client.version>6.7.2</kubernetes-client.version> <fabric8-client.version>6.2.0</fabric8-client.version> </properties> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Kubernetes Clients --> <dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> <version>${kubernetes-client.version}</version> </dependency> <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>${fabric8-client.version}</version> </dependency> <!-- HNC CRD Models --> <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-model</artifactId> <version>${fabric8-client.version}</version> </dependency> </dependencies> HNC Installation
# hnc-installation.yaml apiVersion: v1 kind: Namespace metadata: name: hnc-system --- apiVersion: apps/v1 kind: Deployment metadata: name: hnc-controller-manager namespace: hnc-system spec: replicas: 1 selector: matchLabels: control-plane: hnc-controller-manager template: metadata: labels: control-plane: hnc-controller-manager spec: containers: - args: - --leader-elect - --v=2 command: - /manager image: gcr.io/k8s-staging-multitenancy/hnc/controller:v1.0.0 name: manager resources: limits: cpu: 200m memory: 256Mi requests: cpu: 100m memory: 64Mi
Core HNC Java Implementation
1. HNC Configuration Properties
@ConfigurationProperties(prefix = "hnc") @Data public class HNCProperties { private boolean enabled = true; private String defaultParentNamespace = "org-root"; private boolean enablePropagation = true; private boolean enableInheritance = true; private ResourceInheritance resourceInheritance = new ResourceInheritance(); @Data public static class ResourceInheritance { private boolean configMaps = true; private boolean secrets = true; private boolean roles = true; private boolean roleBindings = true; private boolean networkPolicies = true; private boolean resourceQuotas = true; private boolean limitRanges = true; } } 2. HNC Client Service
@Service @Slf4j public class HNCClientService { private final KubernetesClient kubernetesClient; private final HNCProperties hncProperties; public HNCClientService(HNCProperties hncProperties) { this.hncProperties = hncProperties; this.kubernetesClient = new DefaultKubernetesClient(); } /** * Create a hierarchical namespace with parent-child relationship */ public boolean createHierarchicalNamespace(String namespace, String parentNamespace) { try { // Check if parent namespace exists if (!namespaceExists(parentNamespace)) { log.warn("Parent namespace {} does not exist, creating it", parentNamespace); createNamespace(parentNamespace); } // Create the child namespace createNamespace(namespace); // Create HNC subnamespace anchor createSubnamespaceAnchor(namespace, parentNamespace); log.info("Created hierarchical namespace: {} -> {}", parentNamespace, namespace); return true; } catch (Exception e) { log.error("Failed to create hierarchical namespace {} under {}", namespace, parentNamespace, e); return false; } } /** * Create a regular Kubernetes namespace */ private void createNamespace(String namespace) { kubernetesClient.namespaces().createOrReplace( new NamespaceBuilder() .withNewMetadata() .withName(namespace) .addToLabels("hnc.x-k8s.io/included-namespace", "true") .endMetadata() .build() ); } /** * Create SubnamespaceAnchor CRD for HNC */ private void createSubnamespaceAnchor(String namespace, String parentNamespace) { var anchor = kubernetesClient.customResources( getSubnamespaceAnchorContext() ).inNamespace(parentNamespace).createOrReplace( new GenericKubernetesResourceBuilder() .withApiVersion("hnc.x-k8s.io/v1alpha2") .withKind("SubnamespaceAnchor") .withNewMetadata() .withName(namespace) .endMetadata() .build() ); log.debug("Created subnamespace anchor: {}", anchor); } /** * Get namespace hierarchy tree */ public NamespaceHierarchy getNamespaceHierarchy(String rootNamespace) { try { Map<String, List<String>> hierarchy = new HashMap<>(); buildHierarchy(rootNamespace, hierarchy, new HashSet<>()); return new NamespaceHierarchy(rootNamespace, hierarchy); } catch (Exception e) { log.error("Failed to build namespace hierarchy for {}", rootNamespace, e); return new NamespaceHierarchy(rootNamespace, Collections.emptyMap()); } } private void buildHierarchy(String currentNamespace, Map<String, List<String>> hierarchy, Set<String> visited) { if (visited.contains(currentNamespace)) { return; // Prevent cycles } visited.add(currentNamespace); List<String> children = getChildNamespaces(currentNamespace); hierarchy.put(currentNamespace, children); for (String child : children) { buildHierarchy(child, hierarchy, visited); } } /** * Get all child namespaces of a parent */ public List<String> getChildNamespaces(String parentNamespace) { try { var anchors = kubernetesClient.customResources( getSubnamespaceAnchorContext() ).inNamespace(parentNamespace).list(); return anchors.getItems().stream() .map(anchor -> anchor.getMetadata().getName()) .collect(Collectors.toList()); } catch (Exception e) { log.error("Failed to get child namespaces for {}", parentNamespace, e); return Collections.emptyList(); } } /** * Get parent namespace of a child */ public String getParentNamespace(String childNamespace) { try { // Look for HNC hierarchy configuration var namespace = kubernetesClient.namespaces().withName(childNamespace).get(); if (namespace != null) { String parent = namespace.getMetadata().getAnnotations() .get("hnc.x-k8s.io/parent"); return parent != null ? parent : "none"; } return "none"; } catch (Exception e) { log.error("Failed to get parent namespace for {}", childNamespace, e); return "unknown"; } } /** * Propagate resource to child namespaces */ public boolean propagateResource(String sourceNamespace, String resourceType, String resourceName, List<String> targetNamespaces) { try { for (String targetNamespace : targetNamespaces) { copyResource(sourceNamespace, targetNamespace, resourceType, resourceName); } log.info("Propagated {} {} from {} to {} namespaces", resourceType, resourceName, sourceNamespace, targetNamespaces.size()); return true; } catch (Exception e) { log.error("Failed to propagate resource {}:{} from {}", resourceType, resourceName, sourceNamespace, e); return false; } } private void copyResource(String sourceNamespace, String targetNamespace, String resourceType, String resourceName) { switch (resourceType.toLowerCase()) { case "configmap": copyConfigMap(sourceNamespace, targetNamespace, resourceName); break; case "secret": copySecret(sourceNamespace, targetNamespace, resourceName); break; case "role": copyRole(sourceNamespace, targetNamespace, resourceName); break; default: throw new IllegalArgumentException("Unsupported resource type: " + resourceType); } } private void copyConfigMap(String sourceNamespace, String targetNamespace, String configMapName) { var configMap = kubernetesClient.configMaps() .inNamespace(sourceNamespace) .withName(configMapName) .get(); if (configMap != null) { kubernetesClient.configMaps() .inNamespace(targetNamespace) .createOrReplace( new ConfigMapBuilder(configMap) .editMetadata() .withNamespace(targetNamespace) .withResourceVersion(null) .endMetadata() .build() ); } } private boolean namespaceExists(String namespace) { return kubernetesClient.namespaces().withName(namespace).get() != null; } private CustomResourceDefinitionContext getSubnamespaceAnchorContext() { return new CustomResourceDefinitionContext.Builder() .withName("subnamespaceanchors.hnc.x-k8s.io") .withGroup("hnc.x-k8s.io") .withVersion("v1alpha2") .withPlural("subnamespaceanchors") .withScope("Namespaced") .build(); } } 3. Namespace Hierarchy Model
@Data @AllArgsConstructor public class NamespaceHierarchy { private String rootNamespace; private Map<String, List<String>> children; private Instant createdAt; public NamespaceHierarchy(String rootNamespace, Map<String, List<String>> children) { this.rootNamespace = rootNamespace; this.children = children; this.createdAt = Instant.now(); } public List<String> getAllDescendants(String namespace) { List<String> descendants = new ArrayList<>(); collectDescendants(namespace, descendants); return descendants; } private void collectDescendants(String namespace, List<String> descendants) { List<String> children = this.children.getOrDefault(namespace, Collections.emptyList()); descendants.addAll(children); for (String child : children) { collectDescendants(child, descendants); } } public int getDepth(String namespace) { return calculateDepth(namespace, rootNamespace, 0); } private int calculateDepth(String targetNamespace, String currentNamespace, int currentDepth) { if (targetNamespace.equals(currentNamespace)) { return currentDepth; } List<String> children = this.children.getOrDefault(currentNamespace, Collections.emptyList()); for (String child : children) { int depth = calculateDepth(targetNamespace, child, currentDepth + 1); if (depth != -1) { return depth; } } return -1; // Not found } } @Data public class TenantNamespace { private String name; private String parent; private String tenantId; private String environment; private Map<String, String> labels; private Map<String, String> annotations; private Instant createdAt; private NamespaceStatus status; public enum NamespaceStatus { CREATING, ACTIVE, SUSPENDED, DELETING } } Multi-Tenant Service Implementation
1. Tenant Management Service
@Service @Slf4j public class TenantManagementService { private final HNCClientService hncClient; private final HNCProperties hncProperties; private final KubernetesClient kubernetesClient; public TenantManagementService(HNCClientService hncClient, HNCProperties hncProperties) { this.hncClient = hncClient; this.hncProperties = hncProperties; this.kubernetesClient = new DefaultKubernetesClient(); } /** * Create a complete tenant hierarchy */ public TenantNamespace createTenantHierarchy(String tenantId, String tenantName) { try { // Create tenant root namespace String tenantRootNs = "tenant-" + tenantId; hncClient.createHierarchicalNamespace(tenantRootNs, hncProperties.getDefaultParentNamespace()); // Create environment namespaces String devNamespace = tenantRootNs + "-dev"; String stagingNamespace = tenantRootNs + "-staging"; String productionNamespace = tenantRootNs + "-prod"; hncClient.createHierarchicalNamespace(devNamespace, tenantRootNs); hncClient.createHierarchicalNamespace(stagingNamespace, tenantRootNs); hncClient.createHierarchicalNamespace(productionNamespace, tenantRootNs); // Apply tenant-specific configurations applyTenantConfigurations(tenantRootNs, tenantId, tenantName); log.info("Created tenant hierarchy for {}: {}", tenantId, tenantRootNs); return buildTenantNamespace(tenantRootNs, tenantId, tenantName); } catch (Exception e) { log.error("Failed to create tenant hierarchy for {}", tenantId, e); throw new TenantCreationException("Failed to create tenant: " + tenantId, e); } } /** * Apply tenant-specific configurations and policies */ private void applyTenantConfigurations(String tenantRootNs, String tenantId, String tenantName) { // Create tenant-specific Role createTenantRole(tenantRootNs, tenantId); // Create tenant-specific ResourceQuota createTenantResourceQuota(tenantRootNs, tenantId); // Create tenant-specific NetworkPolicy createTenantNetworkPolicy(tenantRootNs, tenantId); // Create tenant configuration ConfigMap createTenantConfigMap(tenantRootNs, tenantId, tenantName); } private void createTenantRole(String namespace, String tenantId) { var role = new RoleBuilder() .withNewMetadata() .withName(tenantId + "-admin") .withNamespace(namespace) .addToLabels("tenant", tenantId) .endMetadata() .withRules( new PolicyRuleBuilder() .withApiGroups("") .withResources("pods", "services", "configmaps", "secrets") .withVerbs("get", "list", "watch", "create", "update", "delete") .build(), new PolicyRuleBuilder() .withApiGroups("apps") .withResources("deployments", "statefulsets", "replicasets") .withVerbs("get", "list", "watch", "create", "update", "delete") .build() ) .build(); kubernetesClient.rbac().roles().inNamespace(namespace).createOrReplace(role); } private void createTenantResourceQuota(String namespace, String tenantId) { var resourceQuota = new ResourceQuotaBuilder() .withNewMetadata() .withName(tenantId + "-quota") .withNamespace(namespace) .addToLabels("tenant", tenantId) .endMetadata() .withNewSpec() .withHard(Map.of( "requests.cpu", "2", "requests.memory", "4Gi", "limits.cpu", "4", "limits.memory", "8Gi", "pods", "50", "services", "20" )) .endSpec() .build(); kubernetesClient.resourceQuotas().inNamespace(namespace).createOrReplace(resourceQuota); } private void createTenantConfigMap(String namespace, String tenantId, String tenantName) { var configMap = new ConfigMapBuilder() .withNewMetadata() .withName(tenantId + "-config") .withNamespace(namespace) .addToLabels("tenant", tenantId) .endMetadata() .withData(Map.of( "tenant.id", tenantId, "tenant.name", tenantName, "database.host", "db-" + tenantId + ".svc.cluster.local", "cache.host", "cache-" + tenantId + ".svc.cluster.local" )) .build(); kubernetesClient.configMaps().inNamespace(namespace).createOrReplace(configMap); } private void createTenantNetworkPolicy(String namespace, String tenantId) { var networkPolicy = new NetworkPolicyBuilder() .withNewMetadata() .withName(tenantId + "-isolation") .withNamespace(namespace) .addToLabels("tenant", tenantId) .endMetadata() .withNewSpec() .withPodSelector(new LabelSelector()) // All pods in namespace .withPolicyTypes("Ingress", "Egress") .withNewIngress() .addNewFrom() .withPodSelector(new LabelSelectorBuilder() .addToMatchLabels("tenant", tenantId) .build()) .endFrom() .endIngress() .withNewEgress() .addNewTo() .withPodSelector(new LabelSelectorBuilder() .addToMatchLabels("tenant", tenantId) .build()) .endTo() .endEgress() .endSpec() .build(); kubernetesClient.networkPolicies().inNamespace(namespace).createOrReplace(networkPolicy); } /** * Get all namespaces for a tenant */ public List<String> getTenantNamespaces(String tenantId) { String tenantRootNs = "tenant-" + tenantId; NamespaceHierarchy hierarchy = hncClient.getNamespaceHierarchy(tenantRootNs); return hierarchy.getAllDescendants(tenantRootNs); } /** * Delete tenant hierarchy */ public boolean deleteTenantHierarchy(String tenantId) { try { String tenantRootNs = "tenant-" + tenantId; // Get all namespaces in the hierarchy List<String> tenantNamespaces = getTenantNamespaces(tenantId); tenantNamespaces.add(tenantRootNs); // Delete namespaces in reverse order (children first) Collections.reverse(tenantNamespaces); for (String namespace : tenantNamespaces) { kubernetesClient.namespaces().withName(namespace).delete(); log.info("Deleted tenant namespace: {}", namespace); } log.info("Deleted tenant hierarchy for: {}", tenantId); return true; } catch (Exception e) { log.error("Failed to delete tenant hierarchy for {}", tenantId, e); return false; } } private TenantNamespace buildTenantNamespace(String namespace, String tenantId, String tenantName) { return TenantNamespace.builder() .name(namespace) .parent(hncClient.getParentNamespace(namespace)) .tenantId(tenantId) .tenantName(tenantName) .environment(extractEnvironment(namespace)) .labels(Map.of( "tenant", tenantId, "environment", extractEnvironment(namespace) )) .annotations(Map.of( "hnc.x-k8s.io/included-namespace", "true" )) .createdAt(Instant.now()) .status(TenantNamespace.NamespaceStatus.ACTIVE) .build(); } private String extractEnvironment(String namespace) { if (namespace.endsWith("-dev")) return "development"; if (namespace.endsWith("-staging")) return "staging"; if (namespace.endsWith("-prod")) return "production"; return "root"; } } 2. Multi-Tenant Application Service
@Service @Slf4j public class MultiTenantApplicationService { private final HNCClientService hncClient; private final TenantManagementService tenantService; private final KubernetesClient kubernetesClient; public MultiTenantApplicationService(HNCClientService hncClient, TenantManagementService tenantService) { this.hncClient = hncClient; this.tenantService = tenantService; this.kubernetesClient = new DefaultKubernetesClient(); } /** * Deploy application to tenant namespace */ public boolean deployToTenant(String tenantId, String environment, ApplicationDeployment deployment) { try { String targetNamespace = determineTargetNamespace(tenantId, environment); // Validate namespace exists if (!hncClient.namespaceExists(targetNamespace)) { throw new NamespaceNotFoundException( "Target namespace not found: " + targetNamespace); } // Deploy application resources deployApplicationResources(targetNamespace, deployment); // Create service createService(targetNamespace, deployment); // Create ingress if needed if (deployment.isExposePublicly()) { createIngress(targetNamespace, deployment, tenantId); } log.info("Deployed application {} to tenant {} in namespace {}", deployment.getAppName(), tenantId, targetNamespace); return true; } catch (Exception e) { log.error("Failed to deploy application to tenant {}: {}", tenantId, e.getMessage()); return false; } } /** * Deploy application to all environments of a tenant */ public boolean deployToAllEnvironments(String tenantId, ApplicationDeployment deployment) { List<String> environments = List.of("dev", "staging", "prod"); boolean allSuccess = true; for (String env : environments) { boolean success = deployToTenant(tenantId, env, deployment); if (!success) { allSuccess = false; log.warn("Failed to deploy to {} environment for tenant {}", env, tenantId); } } return allSuccess; } private String determineTargetNamespace(String tenantId, String environment) { return String.format("tenant-%s-%s", tenantId, environment); } private void deployApplicationResources(String namespace, ApplicationDeployment deployment) { // Create Deployment var deploymentResource = new DeploymentBuilder() .withNewMetadata() .withName(deployment.getAppName()) .withNamespace(namespace) .addToLabels("app", deployment.getAppName()) .addToLabels("version", deployment.getVersion()) .endMetadata() .withNewSpec() .withReplicas(deployment.getReplicas()) .withNewSelector() .addToMatchLabels("app", deployment.getAppName()) .endSelector() .withNewTemplate() .withNewMetadata() .addToLabels("app", deployment.getAppName()) .addToLabels("version", deployment.getVersion()) .endMetadata() .withNewSpec() .addNewContainer() .withName(deployment.getAppName()) .withImage(deployment.getImage()) .withPorts(deployment.getContainerPorts().stream() .map(port -> new ContainerPortBuilder() .withContainerPort(port) .build()) .collect(Collectors.toList())) .withResources(deployment.getResourceRequirements()) .endContainer() .endSpec() .endTemplate() .endSpec() .build(); kubernetesClient.apps().deployments().inNamespace(namespace) .createOrReplace(deploymentResource); } private void createService(String namespace, ApplicationDeployment deployment) { var service = new ServiceBuilder() .withNewMetadata() .withName(deployment.getAppName() + "-service") .withNamespace(namespace) .endMetadata() .withNewSpec() .withSelector(Map.of("app", deployment.getAppName())) .withPorts(deployment.getContainerPorts().stream() .map(port -> new ServicePortBuilder() .withPort(port) .withTargetPort(new IntOrString(port)) .build()) .collect(Collectors.toList())) .withType("ClusterIP") .endSpec() .build(); kubernetesClient.services().inNamespace(namespace).createOrReplace(service); } private void createIngress(String namespace, ApplicationDeployment deployment, String tenantId) { var ingress = new IngressBuilder() .withNewMetadata() .withName(deployment.getAppName() + "-ingress") .withNamespace(namespace) .addToAnnotations("nginx.ingress.kubernetes.io/rewrite-target", "/") .endMetadata() .withNewSpec() .addNewRule() .withHost(String.format("%s.%s.example.com", deployment.getAppName(), tenantId)) .withNewHttp() .addNewPath() .withPath("/") .withPathType("Prefix") .withNewBackend() .withNewService() .withName(deployment.getAppName() + "-service") .withNewPort() .withNumber(deployment.getContainerPorts().get(0)) .endPort() .endService() .endBackend() .endPath() .endHttp() .endRule() .endSpec() .build(); kubernetesClient.network().ingresses().inNamespace(namespace).createOrReplace(ingress); } /** * Get application status across all tenant environments */ public Map<String, ApplicationStatus> getTenantApplicationStatus(String tenantId, String appName) { Map<String, ApplicationStatus> statusMap = new HashMap<>(); List<String> environments = List.of("dev", "staging", "prod"); for (String env : environments) { String namespace = determineTargetNamespace(tenantId, env); ApplicationStatus status = getApplicationStatus(namespace, appName); statusMap.put(env, status); } return statusMap; } private ApplicationStatus getApplicationStatus(String namespace, String appName) { try { var deployment = kubernetesClient.apps().deployments() .inNamespace(namespace) .withName(appName) .get(); if (deployment == null) { return ApplicationStatus.NOT_FOUND; } Integer availableReplicas = deployment.getStatus().getAvailableReplicas(); Integer desiredReplicas = deployment.getStatus().getReplicas(); if (availableReplicas == null || availableReplicas == 0) { return ApplicationStatus.NOT_READY; } if (availableReplicas.equals(desiredReplicas)) { return ApplicationStatus.READY; } else { return ApplicationStatus.SCALING; } } catch (Exception e) { log.error("Failed to get application status for {} in {}", appName, namespace, e); return ApplicationStatus.UNKNOWN; } } } @Data @Builder class ApplicationDeployment { private String appName; private String version; private String image; private int replicas; private List<Integer> containerPorts; private ResourceRequirements resourceRequirements; private boolean exposePublicly; private Map<String, String> environmentVariables; } enum ApplicationStatus { NOT_FOUND, NOT_READY, SCALING, READY, UNKNOWN } REST Controllers for HNC Management
1. Tenant Management Controller
@RestController @RequestMapping("/api/tenants") @Slf4j public class TenantController { private final TenantManagementService tenantService; private final MultiTenantApplicationService appService; public TenantController(TenantManagementService tenantService, MultiTenantApplicationService appService) { this.tenantService = tenantService; this.appService = appService; } @PostMapping public ResponseEntity<TenantResponse> createTenant(@RequestBody CreateTenantRequest request) { try { TenantNamespace tenantNs = tenantService.createTenantHierarchy( request.getTenantId(), request.getTenantName()); return ResponseEntity.ok(TenantResponse.success(tenantNs)); } catch (Exception e) { log.error("Failed to create tenant: {}", request.getTenantId(), e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(TenantResponse.error(e.getMessage())); } } @DeleteMapping("/{tenantId}") public ResponseEntity<TenantResponse> deleteTenant(@PathVariable String tenantId) { try { boolean success = tenantService.deleteTenantHierarchy(tenantId); if (success) { return ResponseEntity.ok(TenantResponse.success("Tenant deleted successfully")); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(TenantResponse.error("Failed to delete tenant")); } } catch (Exception e) { log.error("Failed to delete tenant: {}", tenantId, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(TenantResponse.error(e.getMessage())); } } @GetMapping("/{tenantId}/namespaces") public ResponseEntity<List<String>> getTenantNamespaces(@PathVariable String tenantId) { try { List<String> namespaces = tenantService.getTenantNamespaces(tenantId); return ResponseEntity.ok(namespaces); } catch (Exception e) { log.error("Failed to get namespaces for tenant: {}", tenantId, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @PostMapping("/{tenantId}/deploy") public ResponseEntity<DeploymentResponse> deployToTenant( @PathVariable String tenantId, @RequestBody DeploymentRequest request) { try { ApplicationDeployment deployment = ApplicationDeployment.builder() .appName(request.getAppName()) .version(request.getVersion()) .image(request.getImage()) .replicas(request.getReplicas()) .containerPorts(request.getPorts()) .resourceRequirements(request.getResources()) .exposePublicly(request.isExposePublicly()) .build(); boolean success; if ("all".equalsIgnoreCase(request.getEnvironment())) { success = appService.deployToAllEnvironments(tenantId, deployment); } else { success = appService.deployToTenant(tenantId, request.getEnvironment(), deployment); } if (success) { return ResponseEntity.ok(DeploymentResponse.success("Deployment initiated")); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(DeploymentResponse.error("Deployment failed")); } } catch (Exception e) { log.error("Failed to deploy to tenant: {}", tenantId, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(DeploymentResponse.error(e.getMessage())); } } @GetMapping("/{tenantId}/apps/{appName}/status") public ResponseEntity<Map<String, ApplicationStatus>> getAppStatus( @PathVariable String tenantId, @PathVariable String appName) { try { Map<String, ApplicationStatus> status = appService .getTenantApplicationStatus(tenantId, appName); return ResponseEntity.ok(status); } catch (Exception e) { log.error("Failed to get app status for tenant {}: {}", tenantId, e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } } @Data class CreateTenantRequest { @NotBlank private String tenantId; @NotBlank private String tenantName; private Map<String, String> labels; private Map<String, String> annotations; } @Data class DeploymentRequest { @NotBlank private String appName; @NotBlank private String version; @NotBlank private String image; private String environment = "dev"; private int replicas = 1; private List<Integer> ports = List.of(8080); private ResourceRequirements resources; private boolean exposePublicly = false; } @Data @AllArgsConstructor class TenantResponse { private boolean success; private String message; private TenantNamespace tenantNamespace; public static TenantResponse success(TenantNamespace tenantNamespace) { return new TenantResponse(true, "Tenant created successfully", tenantNamespace); } public static TenantResponse success(String message) { return new TenantResponse(true, message, null); } public static TenantResponse error(String message) { return new TenantResponse(false, message, null); } } @Data @AllArgsConstructor class DeploymentResponse { private boolean success; private String message; public static DeploymentResponse success(String message) { return new DeploymentResponse(true, message); } public static DeploymentResponse error(String message) { return new DeploymentResponse(false, message); } } 2. HNC Administration Controller
@RestController @RequestMapping("/api/hnc") @Slf4j public class HNCAdminController { private final HNCClientService hncClient; public HNCAdminController(HNCClientService hncClient) { this.hncClient = hncClient; } @PostMapping("/namespaces") public ResponseEntity<String> createHierarchicalNamespace( @RequestBody CreateNamespaceRequest request) { try { boolean success = hncClient.createHierarchicalNamespace( request.getNamespace(), request.getParentNamespace()); if (success) { return ResponseEntity.ok("Namespace created successfully"); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Failed to create namespace"); } } catch (Exception e) { log.error("Failed to create hierarchical namespace", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error: " + e.getMessage()); } } @GetMapping("/hierarchy") public ResponseEntity<NamespaceHierarchy> getNamespaceHierarchy( @RequestParam(defaultValue = "org-root") String rootNamespace) { try { NamespaceHierarchy hierarchy = hncClient.getNamespaceHierarchy(rootNamespace); return ResponseEntity.ok(hierarchy); } catch (Exception e) { log.error("Failed to get namespace hierarchy", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @GetMapping("/namespaces/{namespace}/children") public ResponseEntity<List<String>> getChildNamespaces(@PathVariable String namespace) { try { List<String> children = hncClient.getChildNamespaces(namespace); return ResponseEntity.ok(children); } catch (Exception e) { log.error("Failed to get child namespaces for {}", namespace, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @PostMapping("/propagate") public ResponseEntity<String> propagateResource(@RequestBody PropagateRequest request) { try { boolean success = hncClient.propagateResource( request.getSourceNamespace(), request.getResourceType(), request.getResourceName(), request.getTargetNamespaces() ); if (success) { return ResponseEntity.ok("Resource propagated successfully"); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Failed to propagate resource"); } } catch (Exception e) { log.error("Failed to propagate resource", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error: " + e.getMessage()); } } } @Data class CreateNamespaceRequest { @NotBlank private String namespace; @NotBlank private String parentNamespace; private Map<String, String> labels; } @Data class PropagateRequest { @NotBlank private String sourceNamespace; @NotBlank private String resourceType; // configmap, secret, role @NotBlank private String resourceName; @NotEmpty private List<String> targetNamespaces; } Configuration and Exception Handling
1. Spring Boot Configuration
# application.yaml spring: application: name: hnc-manager hnc: enabled: true default-parent-namespace: org-root enable-propagation: true enable-inheritance: true resource-inheritance: config-maps: true secrets: true roles: true role-bindings: true network-policies: true resource-quotas: true limit-ranges: true management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always logging: level: com.example.hnc: DEBUG
2. Custom Exceptions
public class TenantCreationException extends RuntimeException { public TenantCreationException(String message) { super(message); } public TenantCreationException(String message, Throwable cause) { super(message, cause); } } public class NamespaceNotFoundException extends RuntimeException { public NamespaceNotFoundException(String message) { super(message); } public NamespaceNotFoundException(String message, Throwable cause) { super(message, cause); } } @ControllerAdvice public class HNCExceptionHandler { @ExceptionHandler(TenantCreationException.class) public ResponseEntity<ErrorResponse> handleTenantCreationException(TenantCreationException e) { log.error("Tenant creation failed", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("TENANT_CREATION_FAILED", e.getMessage())); } @ExceptionHandler(NamespaceNotFoundException.class) public ResponseEntity<ErrorResponse> handleNamespaceNotFoundException(NamespaceNotFoundException e) { log.error("Namespace not found", e); return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new ErrorResponse("NAMESPACE_NOT_FOUND", e.getMessage())); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGenericException(Exception e) { log.error("Unexpected error", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred")); } } @Data @AllArgsConstructor class ErrorResponse { private String code; private String message; private Instant timestamp; public ErrorResponse(String code, String message) { this.code = code; this.message = message; this.timestamp = Instant.now(); } } Testing
1. Unit Tests
@ExtendWith(MockitoExtension.class) class TenantManagementServiceTest { @Mock private HNCClientService hncClient; @Mock private KubernetesClient kubernetesClient; @InjectMocks private TenantManagementService tenantService; @Test void shouldCreateTenantHierarchy() { // Given String tenantId = "test-tenant"; String tenantName = "Test Tenant"; when(hncClient.namespaceExists(anyString())).thenReturn(true); when(hncClient.getParentNamespace(anyString())).thenReturn("org-root"); // When TenantNamespace result = tenantService.createTenantHierarchy(tenantId, tenantName); // Then assertThat(result).isNotNull(); assertThat(result.getTenantId()).isEqualTo(tenantId); assertThat(result.getStatus()).isEqualTo(TenantNamespace.NamespaceStatus.ACTIVE); } } @SpringBootTest class HNCIntegrationTest { @Autowired private TenantManagementService tenantService; @Test void shouldManageTenantLifecycle() { // Integration test with test Kubernetes cluster // This would require a test Kubernetes environment } } Best Practices
- Namespace Naming Conventions: Use consistent naming patterns
- Resource Quotas: Set appropriate limits for each tenant
- Security Boundaries: Use NetworkPolicies to isolate tenants
- Monitoring: Track namespace creation and resource usage
- Backup: Implement namespace backup strategies
- Cleanup: Automatically clean up unused tenant namespaces
// Example of automated cleanup @Component @Slf4j public class NamespaceCleanupService { private final TenantManagementService tenantService; @Scheduled(cron = "0 0 2 * * ?") // Daily at 2 AM public void cleanupInactiveTenants() { log.info("Starting namespace cleanup..."); // Implementation to find and delete inactive tenants // Based on last activity, resource usage, etc. } } Conclusion
Hierarchical Namespaces in Java provide:
- Organizational structure for complex multi-tenant environments
- Resource inheritance across namespace hierarchies
- Policy propagation for consistent security and configuration
- Simplified management of large-scale Kubernetes deployments
- Tenant isolation with proper resource boundaries
By implementing the patterns shown above, you can build robust multi-tenant applications that leverage HNC for better organization, security, and management of Kubernetes resources across different teams, projects, or customers.
Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)
https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.
https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.
https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.
https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.
https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.
https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.
https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.
https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.
https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.