Skip to content

Commit 934220c

Browse files
authored
Merge pull request #1354 from yue9944882/doc/kubectl
Doc: Kubectl Equivalence in Java
2 parents 2ebac6d + 89f9aa9 commit 934220c

File tree

2 files changed

+260
-4
lines changed

2 files changed

+260
-4
lines changed

docs/kubectl-equivalence-in-java.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#Kubectl Equivalence in Java
2+
3+
__TL;DR__: Used to kubectl? Now our kubernetes Java client library has released a set of
4+
helpful client utilities which has the similar input argument interface as the kubectl binary.
5+
Especially the developers who're already familiar with kubectl commands, after reading this
6+
document, you will know how to build programs that interact with Kubernetes as easily as kubectl.
7+
8+
9+
### What is Java Kubectl
10+
11+
The Java Kubectl is not only a more user-friendly wrapper for our direct HTTP kubernetes client,
12+
but also contains implementation of commonly-used kubectl advanced commands. All these kubectl
13+
equivalences are accessible as a group of static helper functions under `io.kubernetes.client.extended.kubectl.Kubectl`
14+
class. You can import them by adding the following dependency to your project:
15+
16+
```xml
17+
<dependency>
18+
<groupId>io.kubernetes</groupId>
19+
<artifactId>client-java-extended</artifactId>
20+
<version>${latest project version}</version>
21+
</dependency>
22+
```
23+
24+
Now you're all set, invoke the kubectl commands as Java static functions wherever you like in
25+
your project.
26+
27+
#### (Optional) Setting a Global Client-Config
28+
29+
Kubectl static helpers don't know its client-config (or kubeconfig if the name makes more sense to
30+
you) unless you set it when your application starts. You can either specifies the client-config upon
31+
invoking kubectl helpers or simply set a global config at the start of your application:
32+
33+
```java
34+
Configuration.setDefaultApiClient(ClientBuilder.defaultClient());
35+
```
36+
37+
This will create a client on either a client, or a container in Kubernetes.
38+
39+
### Manifest of Supported Commands
40+
41+
42+
#### Kubectl get
43+
44+
You can either query a single resource or list multiple resources using the `Kubectl#get` helper depending
45+
on whether you're passing `name()` in the flow. Here is a few illustrative examples for querying pod
46+
resources:
47+
48+
```java
49+
// kubectl get -n default pod foo
50+
V1Pod pod = Kubectl.get(V1Pod.class)
51+
.namespace("default")
52+
.name("foo")
53+
.execute();
54+
// kubectl get -n default pod
55+
List<V1Pod> pods = Kubectl.get(V1Pod.class)
56+
.namespace("default")
57+
.execute();
58+
// kubectl get pod --all-namespaces
59+
List<V1Pod> pods = Kubectl.get(V1Pod.class)
60+
.execute();
61+
```
62+
63+
#### Kubectl create
64+
65+
Currently the `Kubectl#create` helper only accepts a desired instance of resource object, you need to do the
66+
deserialization of a resource object or manually craft an instance of the resource.
67+
68+
```java
69+
// kubectl create -f <file>
70+
V1Pod creatingPod = /* load it from file or else */;
71+
V1Pod createdPod = Kubectl.create(V1Pod.class)
72+
.resource(creatingPod)
73+
.execute();
74+
```
75+
76+
#### Kubectl delete
77+
78+
`Kubectl#delete` works the same as `kubectl delete` command.
79+
80+
```java
81+
// kubectl delete -n default pod foo
82+
V1Pod deletedPod = Kubectl.delete(V1Pod.class)
83+
.namespace("default")
84+
.name("foo")
85+
.execute();
86+
```
87+
88+
#### Kubectl patch
89+
90+
`Kubectl#patch` works the same as `kubectl patch` command.
91+
92+
```java
93+
// kubectl patch --type='strategic' --patch="{\"metadata\":{\"labels\":{\"foo\":\"bar\"}}"
94+
V1Pod patchedPod = Kubectl.patch(V1Pod.class)
95+
.patchType(V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH)
96+
.patchContent(new V1Patch("{\"metadata\":{\"labels\":{\"foo\":\"bar\"}}"))
97+
.execute();
98+
```
99+
100+
#### Kubectl apply
101+
102+
Note that for now only server-side apply is supported, so your apiserver version is required to
103+
be greater than or equal to 1.18.0. `Kubectl#apply` works the same as `kubectl apply --server-side=true`.
104+
105+
```java
106+
// kubectl apply --server-side=true --field-manager=java-kubectl --force-conflict=true -f <file>
107+
V1Pod applyingPod = /* load it from file or else */;
108+
V1Pod appliedPod = Kubectl.apply(V1Pod.class)
109+
.fieldManager("java-kubectl")
110+
.forceConflict(true)
111+
.resource(applyingPod)
112+
.execute();
113+
```
114+
115+
#### Kubectl scale
116+
117+
```java
118+
// kubectl scale -n default rs foo --replicas=2
119+
V1ReplicaSet scaledRs = Kubectl.scale(V1ReplicaSet.class)
120+
.namespace("default")
121+
.name("foo")
122+
.replicas(2)
123+
.execute();
124+
```
125+
126+
127+
#### Kubectl drain
128+
129+
```java
130+
// kubectl drain node1
131+
V1Node drainedNode = Kubectl.drain()
132+
.name("node1")
133+
.execute();
134+
```
135+
136+
137+
#### Kubectl cordon/uncordon
138+
139+
```java
140+
// kubectl cordon node1
141+
V1Node cordondNode = Kubectl.cordon()
142+
.name("node1")
143+
.execute();
144+
// kubectl uncordon node1
145+
V1Node uncordondNode = Kubectl.uncordon()
146+
.name("node1")
147+
.execute();
148+
```
149+
150+
#### Kubectl taint
151+
152+
```java
153+
// kubectl taint nodes foo dedicated:NoSchedule
154+
V1Node taintedNode = Kubectl.taint()
155+
.addTaint("dedicated", "NoSchedule")
156+
.execute()
157+
// kubectl taint nodes foo dedicated=special-user:NoSchedule
158+
V1Node taintedNode = Kubectl.taint()
159+
.addTaint("dedicated", "special-user", "NoSchedule")
160+
.execute()
161+
// kubectl taint nodes foo dedicated:NoSchedule-
162+
V1Node taintedNode = Kubectl.taint()
163+
.removeTaint("dedicated", "NoSchedule")
164+
.execute()
165+
```
166+
167+
168+
169+
#### Kubectl label/annotate
170+
171+
```java
172+
// kubectl label -n default pod foo key1=value1 key2=value2
173+
V1Pod labelledPod = Kubectl.label(V1Pod)
174+
.addLabel("key1", "value1")
175+
.addLabel("key2", "value2")
176+
.namespace("default")
177+
.name("foo")
178+
.execute();
179+
// kubectl annotate -n default pod foo key1=value1 key2=value2
180+
V1Pod annotatedPod = Kubectl.annotate(V1Pod)
181+
.addLabel("key1", "value1")
182+
.addLabel("key2", "value2")
183+
.namespace("default")
184+
.name("foo")
185+
.execute();
186+
```
187+
188+
#### Kubectl api-resources
189+
190+
```java
191+
// kubectl api-resources
192+
Set<Discovery.APIResource> apiResourceSet = Kubectl.apiResources()
193+
.execute()
194+
```
195+
196+
#### Kubectl exec
197+
198+
```java
199+
// kubectl exec -n default foo -c test-container echo test
200+
int retCode = Kubectl.exec()
201+
.namespace("default")
202+
.name("foo")
203+
.container("test-container")
204+
.command(new String[]{"echo","test"})
205+
.execute();
206+
```
207+
208+
209+
#### Kubectl logs
210+
211+
```java
212+
// kubectl logs -n default foo -c test-container
213+
InputStream logStream = Kubectl.log()
214+
.namespace("default")
215+
.name("foo")
216+
.container("test-container")
217+
.execute();
218+
```
219+
220+
#### Kubectl top
221+
222+
```java
223+
// kubectl top node
224+
List<Pair<V1Node, NodeMetrics>> metrics = Kubectl.top(V1Node.class, NodeMetrics.class)
225+
.metric("cpu")
226+
.execute();
227+
```
228+
229+
### Advanced Tips
230+
231+
#### API Discovery for custom models
232+
233+
Kubernetes allows you to extend new kubernetes API types by either [CustomResourceDefinition](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/)
234+
or [APIServerAggregation](https://kubernetes.io/docs/tasks/extend-kubernetes/setup-extension-api-server/),
235+
and api-discovery is basically a process of discovering new kubernetes resources types from the client-side.
236+
The java client is managing all the api-discovery information at `io.kubernetes.client.util.ModelMapper`.
237+
In order to make the java client know the connection between new kubernetes api and custom Java models, you're
238+
supposed to manually set up the mappings for them by:
239+
240+
```java
241+
ModelMapper.addModelMap(
242+
"example.io", // api-group
243+
"v1", // api-version
244+
"Foo", // kind name -- camel-case'd singular resource name.
245+
"foos", // resource name -- lowercase plural resource name
246+
true, // is namespace-scoped
247+
Foo.class); // java model class
248+
```

extended/src/main/java/io/kubernetes/client/extended/kubectl/KubectlPatch.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,16 @@
1212
*/
1313
package io.kubernetes.client.extended.kubectl;
1414

15-
import io.kubernetes.client.common.KubernetesListObject;
1615
import io.kubernetes.client.common.KubernetesObject;
1716
import io.kubernetes.client.custom.V1Patch;
17+
import io.kubernetes.client.util.ModelMapper;
1818
import io.kubernetes.client.util.generic.GenericKubernetesApi;
1919

2020
public class KubectlPatch<ApiType extends KubernetesObject>
2121
extends Kubectl.ResourceBuilder<ApiType, KubectlPatch<ApiType>>
2222
implements Kubectl.Executable<ApiType> {
2323

24-
private Class<ApiType> apiTypeClass;
25-
private Class<KubernetesListObject> apiListTypeClass;
24+
private String patchType;
2625
private V1Patch patchContent;
2726

2827
KubectlPatch(Class<ApiType> apiTypeClass) {
@@ -34,9 +33,18 @@ public KubectlPatch patchContent(V1Patch patchContent) {
3433
return this;
3534
}
3635

36+
public KubectlPatch patchType(String patchType) {
37+
this.patchType = patchType;
38+
return this;
39+
}
40+
3741
@Override
3842
public ApiType execute() {
3943
GenericKubernetesApi genericKubernetesApi = getGenericApi();
40-
return (ApiType) genericKubernetesApi.patch(namespace, name, patchContent);
44+
if (ModelMapper.isNamespaced(apiTypeClass)) {
45+
return (ApiType) genericKubernetesApi.patch(namespace, name, patchType, patchContent);
46+
} else {
47+
return (ApiType) genericKubernetesApi.patch(name, patchType, patchContent);
48+
}
4149
}
4250
}

0 commit comments

Comments
 (0)