Implement Kubernetes Service Dependency Queries
Company: Apple
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates proficiency with data structures and algorithms for service inventory operations, including deterministic filtering, attribute matching, graph traversal for transitive dependency resolution, and status propagation across service graphs.
Part 1: Filter Kubernetes Services
Constraints
- 0 <= len(services) <= 10000
- Each service ID is unique
- Each service has at most 50 attributes
- Status is one of 'Healthy', 'Degraded', 'Down', or 'Unknown'
Examples
Input: ([{'id': 'ns1/b', 'name': 'b', 'namespace': 'ns1', 'attributes': {'tier': 'backend'}, 'status': 'Degraded', 'dependencies': []}, {'id': 'ns1/a', 'name': 'a', 'namespace': 'ns1', 'attributes': {'tier': 'backend', 'app': 'api'}, 'status': 'Healthy', 'dependencies': ['ns1/b']}, {'id': 'ns2/c', 'name': 'c', 'namespace': 'ns2', 'attributes': {'tier': 'frontend'}, 'status': 'Healthy', 'dependencies': []}, {'id': 'ns1/d', 'name': 'd', 'namespace': 'ns1', 'attributes': {'team': 'platform'}, 'status': 'Down', 'dependencies': []}], {'namespace': 'ns1', 'required_attributes': {'tier': 'backend'}, 'allowed_statuses': ['Healthy', 'Degraded']})
Expected Output: ['ns1/a', 'ns1/b']
Explanation: Only ns1/a and ns1/b are in namespace ns1, have tier=backend, and have an allowed status.
Input: ([{'id': 'z', 'name': 'z', 'namespace': 'ops', 'attributes': {}, 'status': 'Unknown', 'dependencies': []}, {'id': 'a', 'name': 'a', 'namespace': 'default', 'attributes': {}, 'status': 'Healthy', 'dependencies': []}], {})
Expected Output: ['a', 'z']
Explanation: With no filters, all services match and are returned in sorted order.
Hints
- Check each optional filter independently; missing filters should not block a match.
- For required attributes, every key-value pair must be present in the service's attributes map.
Part 2: Transitive Dependency Status Lookup
Constraints
- 0 <= len(services) <= 10000
- The total number of dependency edges is at most 20000
- Each service ID is unique
- Cycles may exist in the dependency graph
Examples
Input: ([{'id': 'a', 'name': 'a', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['b', 'c']}, {'id': 'b', 'name': 'b', 'namespace': 'ns', 'attributes': {}, 'status': 'Degraded', 'dependencies': ['d']}, {'id': 'c', 'name': 'c', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['b', 'e']}, {'id': 'd', 'name': 'd', 'namespace': 'ns', 'attributes': {}, 'status': 'Down', 'dependencies': ['a']}], 'a')
Expected Output: [{'id': 'b', 'status': 'Degraded'}, {'id': 'd', 'status': 'Down'}, {'id': 'c', 'status': 'Healthy'}, {'id': 'e', 'status': 'Missing'}]
Explanation: Traversal discovers b, then d, then c, then missing e. The cycle back to a is ignored.
Input: ([{'id': 'solo', 'name': 'solo', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': []}], 'solo')
Expected Output: []
Explanation: A service with no dependencies has an empty dependency chain.
Hints
- Build a map from service ID to service data so lookups are O(1).
- Mark IDs as seen before exploring children so cycles and repeated references do not cause duplicate work.
Part 3: List Statuses Along Every Dependency Path
Constraints
- 1 <= len(services) <= 200 for typical cases
- The total number of returned paths is guaranteed to fit in memory
- Cycles may exist
- Each service ID is unique
Examples
Input: ([{'id': 'a', 'name': 'a', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['b', 'c']}, {'id': 'b', 'name': 'b', 'namespace': 'ns', 'attributes': {}, 'status': 'Degraded', 'dependencies': ['d']}, {'id': 'c', 'name': 'c', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['e', 'f']}, {'id': 'd', 'name': 'd', 'namespace': 'ns', 'attributes': {}, 'status': 'Down', 'dependencies': []}, {'id': 'e', 'name': 'e', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['a']}, {'id': 'f', 'name': 'f', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['x']}], 'a')
Expected Output: [[{'id': 'a', 'status': 'Healthy'}, {'id': 'b', 'status': 'Degraded'}, {'id': 'd', 'status': 'Down'}], [{'id': 'a', 'status': 'Healthy'}, {'id': 'c', 'status': 'Healthy'}, {'id': 'e', 'status': 'Healthy'}, {'id': 'a', 'status': 'Cycle'}], [{'id': 'a', 'status': 'Healthy'}, {'id': 'c', 'status': 'Healthy'}, {'id': 'f', 'status': 'Healthy'}, {'id': 'x', 'status': 'Missing'}]]
Explanation: The result shows one normal leaf path, one cycle-terminated path, and one missing-dependency path.
Input: ([{'id': 'solo', 'name': 'solo', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': []}], 'solo')
Expected Output: [[{'id': 'solo', 'status': 'Healthy'}]]
Explanation: A leaf root still produces one path containing the root.
Hints
- Use DFS with backtracking and keep a set of IDs currently on the path to detect cycles.
- A shared dependency can appear in multiple output paths if it is reached through different branches.
Part 4: Prune Noisy Tool Results for an AI Agent
Constraints
- 0 <= len(services) <= 10000
- 0 <= len(candidate_ids) <= 10000
- The total number of dependency edges is at most 20000
- 0 <= max_results <= len(candidate_ids) + 1
Examples
Input: ([{'id': 'a', 'name': 'a', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['b', 'c']}, {'id': 'b', 'name': 'b', 'namespace': 'ns', 'attributes': {}, 'status': 'Down', 'dependencies': []}, {'id': 'c', 'name': 'c', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['d']}, {'id': 'd', 'name': 'd', 'namespace': 'ns', 'attributes': {}, 'status': 'Degraded', 'dependencies': []}, {'id': 'e', 'name': 'e', 'namespace': 'ns', 'attributes': {}, 'status': 'Down', 'dependencies': []}], 'a', ['x', 'c', 'd', 'b', 'e', 'b'], 3)
Expected Output: [{'id': 'b', 'status': 'Down', 'distance': 1}, {'id': 'd', 'status': 'Degraded', 'distance': 2}, {'id': 'c', 'status': 'Healthy', 'distance': 1}]
Explanation: Only b, c, and d are both present and reachable from a. They are sorted by severity, then distance.
Input: ([{'id': 'a', 'name': 'a', 'namespace': 'ns', 'attributes': {}, 'status': 'Healthy', 'dependencies': ['b']}, {'id': 'b', 'name': 'b', 'namespace': 'ns', 'attributes': {}, 'status': 'Unknown', 'dependencies': []}], 'a', ['a', 'b'], 2)
Expected Output: [{'id': 'b', 'status': 'Unknown', 'distance': 1}, {'id': 'a', 'status': 'Healthy', 'distance': 0}]
Explanation: Both are reachable, but Unknown ranks ahead of Healthy.
Hints
- Use BFS from the root to compute shortest distances in an unweighted dependency graph.
- Filtering by reachability before sorting is an effective way to remove irrelevant results.