Skip to content

Commit 87419d4

Browse files
authored
Merge pull request #56 from renoki-co/feature/affinity
[feature] Pod & Node Affinity
2 parents 23c6889 + 6129964 commit 87419d4

13 files changed

+620
-16
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ This package is Work in Progress and while there is in active development, PRs a
7373

7474
Each existent resource has its own documentation, filled with examples:
7575

76+
- [Nodes](docs/kinds/Node.md)
7677
- [Namespaces](docs/kinds/Namespace.md)
7778
- [Config Maps](docs/kinds/ConfigMap.md)
7879
- [Secrets](docs/kinds/Secret.md)

docs/RESOURCES.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
Instances are custom classes that makes the build of containers, for example, more object-oriented that passing an array.
1212

13+
- [Affinity](instances/Affinity.md) - used to declare affinities and anti-affinities
1314
- [Container](instances/Container.md) - used for Pods & Templates
1415
- [Container Probes](instances/Probes.md) - used for Pods' Probes
16+
- [Expressions](instances/Expression.md) - used for various match/fields expressions
1517
- [Resource Metrics](instances/Metrics.md) - used for Horizontal Pod Autoscalers
1618
- [Rules](instances/Rules.md) - used for Roles & Cluster Roles
1719
- [Volumes](instances/Volumes.md) - used for mounting volumes in pods and containers
@@ -22,6 +24,7 @@ Each resource inherits a default "base" class that is making the Resource build-
2224

2325
**Check the documentation for [General Resources](kinds/Resource.md) and [K8s API Usage](Usage.md) before diving in to the actual resources documentation.**
2426

27+
- [Nodes](kinds/Node.md)
2528
- [Namespaces](kinds/Namespace.md)
2629
- [Config Maps](kinds/ConfigMap.md)
2730
- [Secrets](kinds/Secret.md)
@@ -60,11 +63,6 @@ The following list of resources are work in progress and they will be available
6063
- poddisruptionbudgets
6164
- podsecuritypolicies
6265

63-
The following concepts are work in progress as instances:
64-
65-
- pod affinity
66-
- node affinity
67-
6866
# Discussable
6967

7068
The following list of resources might not be useful for the basic needs, so they will be gladly accepted via PR in case there is a need of the resources or they might get discussed and implemented after further reasearch on the structure of the resource.

docs/instances/Affinity.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Affinity
2+
3+
Affinities work with the help of [Expressions](Expression.md) to specify inclusions and exclusions for particular needs.
4+
5+
## preferredDuringSchedulingIgnoredDuringExecution
6+
7+
`preferredDuringSchedulingIgnoredDuringExecution` option is exposed in the affinity instance as `addPreference`:
8+
9+
```php
10+
use RenokiCo\PhpK8s\K8s;
11+
12+
$az = K8s::expression()->in('azname', ['us-east-1a', 'us-east-1b']);
13+
$tier = K8s::expression()->in('tier', ['backend']);
14+
15+
$affinity = K8s::affinity()->addPreference([$az], [], 100); // weight: 100
16+
```
17+
18+
For `nodeSelectorTerms`, you can use `addNodeSelectorPreference()` with the same parameters.
19+
20+
## requiredDuringSchedulingIgnoredDuringExecution
21+
22+
`requiredDuringSchedulingIgnoredDuringExecution` option is exposed in the affinity instance as `addNodeRequirement`:
23+
24+
```php
25+
use RenokiCo\PhpK8s\K8s;
26+
27+
$az = K8s::expression()->in('azname', ['us-east-1a', 'us-east-1b']);
28+
$tier = K8s::expression()->in('tier', ['backend']);
29+
30+
$affinity = K8s::affinity()->addNodeRequirement([$az], [$type]); // requires AZ and tier: backend
31+
```
32+
33+
For Label Selector requirement, use `addLabelSelectorRequirement`:
34+
35+
```php
36+
use RenokiCo\PhpK8s\K8s;
37+
38+
$az = K8s::expression()->in('azname', ['us-east-1a', 'us-east-1b']);
39+
$tier = K8s::expression()->in('tier', ['backend']);
40+
41+
// requires AZ and tier: backend with given topology
42+
$affinity = K8s::affinity()->addLabelSelectorRequirement([$az], [$type], 'aws.amazonaws.io/some-topology');
43+
```

docs/instances/Expression.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Expression
2+
3+
## In & Not In
4+
5+
```php
6+
use RenokiCo\PhpK8s\K8s;
7+
8+
K8s::expression()->in('some-key', ['value1', 'value2']);
9+
10+
K8s::expression()->notIn('some-key', ['value1', 'value2']);
11+
```
12+
13+
## Exists & Does Not Exist
14+
15+
```php
16+
use RenokiCo\PhpK8s\K8s;
17+
18+
K8s::expression()->exists('some-key');
19+
20+
K8s::expression()->doesNotexist('some-key');
21+
```
22+
23+
## Greater & Less Than
24+
25+
```php
26+
use RenokiCo\PhpK8s\K8s;
27+
28+
K8s::expression()->greaterThan('some-key', '1');
29+
30+
K8s::expression()->lessThan('some-key', '1');
31+
```

docs/kinds/Node.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Node
2+
3+
- [Official Documentation](https://kubernetes.io/docs/concepts/architecture/nodes/)
4+
5+
## Example
6+
7+
```php
8+
$nodes = K8s::node()->all();
9+
10+
foreach ($nodes as $node) {
11+
$node->getInfo();
12+
13+
$node->getImages();
14+
15+
$node->getCapacity();
16+
17+
$node->getAllocatableInfo();
18+
}
19+
```

docs/kinds/Pod.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ $pod = $cluster->pod()
3535

3636
Pods can attach volumes so that container can mount them. Please check the [Container documentation](../instances/Container.md) where you can find details on how to attach volumes for different drivers.
3737

38+
## Attaching affinities & anti-affinities
39+
40+
Pods can declare `affinity` to handle pod and node affinities and anti-affinities. Check [Affinity documentation](../instances/Affinity.md) to read more about the pod affinity and anti-affinity declarations.
41+
42+
You can simply attach affinities for both pod and node by calling specialized methods:
43+
44+
```php
45+
$pod->setPodAffinity($affinity);
46+
$pod->setNodeAffinity($affinity);
47+
```
48+
3849
## Container Retrieval
3950

4051
Retrieving the containers and init containers can be retrieved as an array of `\RenokiCo\PhpK8s\Instances\Container` classes or as an array.
@@ -59,9 +70,7 @@ foreach ($containers as $container) {
5970

6071
## Pod Logs
6172

62-
Pods can contain logs, and PHP K8s is good at it. Before checking how it works, please see the [Live Tracking](../../README.md#live-tracking) section from README to see how the closures really work at interpreting the real-time data in Kubernetes.
63-
64-
Retrieve a single string with all logs until the point of call:
73+
Pods can contain logs, and PHP K8s is good at it. You can retrieve a single string with all logs until the point of call:
6574

6675
```php
6776
// Simple logging, no watcher

src/Instances/Affinity.php

+97-8
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,82 @@ class Affinity extends Instance
88
* Add a preference affinity.
99
*
1010
* @param array $expressions
11+
* @param array $fieldsExpressions
1112
* @param int $weight
1213
* @return $this
1314
*/
14-
public function addPreference(array $expressions, int $weight = 1)
15+
public function addPreference(array $expressions, array $fieldsExpressions, int $weight = 1)
1516
{
1617
foreach ($expressions as &$expression) {
1718
if ($expression instanceof Expression) {
1819
$expression = $expression->toArray();
1920
}
2021
}
2122

23+
foreach ($fieldsExpressions as &$expression) {
24+
if ($expression instanceof Expression) {
25+
$expression = $expression->toArray();
26+
}
27+
}
28+
29+
$preference = [
30+
'matchExpressions' => $expressions,
31+
];
32+
33+
if ($fieldsExpressions) {
34+
$preference['matchFields'] = $fieldsExpressions;
35+
}
36+
2237
return $this->addToAttribute('preferredDuringSchedulingIgnoredDuringExecution', [
2338
'weight' => $weight,
24-
'preference' => [
25-
'matchExpressions' => $expressions,
26-
],
39+
'preference' => $preference,
40+
]);
41+
}
42+
43+
/**
44+
* Add a preference affinity for nodeSelector.
45+
*
46+
* @param array $expressions
47+
* @param array $fieldsExpressions
48+
* @param int $weight
49+
* @return $this
50+
*/
51+
public function addNodeSelectorPreference(array $expressions, array $fieldsExpressions, int $weight = 1)
52+
{
53+
foreach ($expressions as &$expression) {
54+
if ($expression instanceof Expression) {
55+
$expression = $expression->toArray();
56+
}
57+
}
58+
59+
foreach ($fieldsExpressions as &$expression) {
60+
if ($expression instanceof Expression) {
61+
$expression = $expression->toArray();
62+
}
63+
}
64+
65+
$preference = [
66+
'matchExpressions' => $expressions,
67+
];
68+
69+
if ($fieldsExpressions) {
70+
$preference['matchFields'] = $fieldsExpressions;
71+
}
72+
73+
return $this->addToAttribute('preferredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms', [
74+
'weight' => $weight,
75+
'preference' => $preference,
2776
]);
2877
}
2978

3079
/**
31-
* Add a required affinity.
80+
* Add a required affinity for nodeSelector.
3281
*
3382
* @param array $expressions
3483
* @param array $fieldsExpressions
3584
* @return $this
3685
*/
37-
public function addRequire(array $expressions, array $fieldsExpressions)
86+
public function addNodeRequirement(array $expressions, array $fieldsExpressions)
3887
{
3988
foreach ($expressions as &$expression) {
4089
if ($expression instanceof Expression) {
@@ -48,10 +97,50 @@ public function addRequire(array $expressions, array $fieldsExpressions)
4897
}
4998
}
5099

100+
$requirement = [
101+
'matchExpressions' => $expressions,
102+
];
103+
51104
if ($fieldsExpressions) {
52-
$this->addToAttribute('requiredDuringSchedulingIgnoredDuringExecution.matchFields', $fieldsExpressions);
105+
$requirement['matchFields'] = $fieldsExpressions;
53106
}
54107

55-
return $this->addToAttribute('requiredDuringSchedulingIgnoredDuringExecution.matchExpressions', $expressions);
108+
return $this->addToAttribute('requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms', $requirement);
109+
}
110+
111+
/**
112+
* Add a required affinity for nodeSelector.
113+
*
114+
* @param array $expressions
115+
* @param array $fieldsExpressions
116+
* @param string $topologyKey
117+
* @return $this
118+
*/
119+
public function addLabelSelectorRequirement(array $expressions, array $fieldsExpressions, string $topologyKey)
120+
{
121+
foreach ($expressions as &$expression) {
122+
if ($expression instanceof Expression) {
123+
$expression = $expression->toArray();
124+
}
125+
}
126+
127+
foreach ($fieldsExpressions as &$expression) {
128+
if ($expression instanceof Expression) {
129+
$expression = $expression->toArray();
130+
}
131+
}
132+
133+
$requirement = [
134+
'matchExpressions' => $expressions,
135+
];
136+
137+
if ($fieldsExpressions) {
138+
$requirement['matchFields'] = $fieldsExpressions;
139+
}
140+
141+
return $this->addToAttribute('requiredDuringSchedulingIgnoredDuringExecution', [
142+
'labelSelector' => $requirement,
143+
'topologyKey' => $topologyKey,
144+
]);
56145
}
57146
}

src/K8s.php

+34
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
class K8s
66
{
7+
/**
8+
* Create a new Node kind.
9+
*
10+
* @param \RenokiCo\PhpK8s\KubernetesCluster|null $cluster
11+
* @param array $attributes
12+
* @return \RenokiCo\PhpK8s\Kinds\K8sNode
13+
*/
14+
public static function node($cluster = null, array $attributes = [])
15+
{
16+
return new Kinds\K8sNode($cluster, $attributes);
17+
}
18+
719
/**
820
* Create a new Namespace kind.
921
*
@@ -321,6 +333,28 @@ public static function volume(array $attributes = [])
321333
return new Instances\Volume($attributes);
322334
}
323335

336+
/**
337+
* Create a new affinity instance.
338+
*
339+
* @param array $attributes
340+
* @return \RenokiCo\PhpK8s\Instances\Affinity
341+
*/
342+
public static function affinity(array $attributes = [])
343+
{
344+
return new Instances\Affinity($attributes);
345+
}
346+
347+
/**
348+
* Create a new expression instance.
349+
*
350+
* @param array $attributes
351+
* @return \RenokiCo\PhpK8s\Instances\Expression
352+
*/
353+
public static function expression(array $attributes = [])
354+
{
355+
return new Instances\Expression($attributes);
356+
}
357+
324358
/**
325359
* Load Kind configuration from an YAML text.
326360
*

0 commit comments

Comments
 (0)