Skip to content

Commit 0e5c1bf

Browse files
authored
Merge pull request #1157 from brendandburns/taint
Add support for taints and kubectl taint.
2 parents 8cdf864 + 2dd1769 commit 0e5c1bf

File tree

5 files changed

+353
-0
lines changed

5 files changed

+353
-0
lines changed

examples/src/main/java/io/kubernetes/client/examples/KubectlExample.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static io.kubernetes.client.extended.kubectl.Kubectl.log;
2020
import static io.kubernetes.client.extended.kubectl.Kubectl.portforward;
2121
import static io.kubernetes.client.extended.kubectl.Kubectl.scale;
22+
import static io.kubernetes.client.extended.kubectl.Kubectl.taint;
2223
import static io.kubernetes.client.extended.kubectl.Kubectl.version;
2324

2425
import com.google.common.io.ByteStreams;
@@ -119,6 +120,39 @@ public static void main(String[] args)
119120
}
120121
System.out.println("Copied " + from + " -> " + to);
121122
System.exit(0);
123+
case "taint":
124+
name = args[1];
125+
String taintSpec = args[2];
126+
boolean remove = taintSpec.endsWith("-");
127+
int ix = taintSpec.indexOf("=");
128+
int ix2 = taintSpec.indexOf(":");
129+
130+
if (remove) {
131+
taintSpec = taintSpec.substring(0, taintSpec.length() - 2);
132+
String key = ix == -1 ? taintSpec : taintSpec.substring(0, ix);
133+
String effect = ix == -1 ? null : taintSpec.substring(ix + 1);
134+
135+
if (effect == null) {
136+
taint(client).name(name).removeTaint(key).execute();
137+
} else {
138+
taint(client).name(name).removeTaint(key, effect).execute();
139+
}
140+
System.exit(0);
141+
}
142+
if (ix2 == -1) {
143+
System.err.println("key:effect or key=value:effect is required.");
144+
System.exit(-1);
145+
}
146+
String key = taintSpec.substring(0, ix == -1 ? ix2 : ix);
147+
String value = ix == -1 ? null : taintSpec.substring(ix + 1, ix2);
148+
String effect = taintSpec.substring(ix2 + 1);
149+
150+
if (value == null) {
151+
taint(client).name(name).addTaint(key, effect).execute();
152+
} else {
153+
taint(client).name(name).addTaint(key, value, effect).execute();
154+
}
155+
System.exit(0);
122156
case "portforward":
123157
name = args[1];
124158
KubectlPortForward forward = portforward(client).name(name).namespace(ns);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@
2323
*/
2424
public class Kubectl {
2525

26+
/** Equivalence for `kubectl taint`. */
27+
public static KubectlTaint taint() {
28+
return taint(Configuration.getDefaultApiClient());
29+
}
30+
31+
/**
32+
* Equivalence for `kubectl taint`
33+
*
34+
* @param apiClient The client to use
35+
*/
36+
public static KubectlTaint taint(ApiClient apiClient) {
37+
return new KubectlTaint(apiClient);
38+
}
39+
2640
/**
2741
* Equivalence for `kubectl cp`.
2842
*
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.extended.kubectl;
14+
15+
import io.kubernetes.client.extended.kubectl.exception.KubectlException;
16+
import io.kubernetes.client.openapi.ApiClient;
17+
import io.kubernetes.client.openapi.ApiException;
18+
import io.kubernetes.client.openapi.apis.CoreV1Api;
19+
import io.kubernetes.client.openapi.models.V1Node;
20+
import io.kubernetes.client.util.taints.Taints;
21+
import io.kubernetes.client.util.taints.Taints.Effect;
22+
import io.kubernetes.client.util.taints.Taints.TaintsBuilder;
23+
import java.io.IOException;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import org.apache.commons.lang3.tuple.ImmutablePair;
27+
import org.apache.commons.lang3.tuple.Pair;
28+
29+
public class KubectlTaint extends Kubectl.ResourceBuilder<V1Node, KubectlTaint>
30+
implements Kubectl.Executable<V1Node> {
31+
32+
private final Map<String, Pair<String, String>> addingTaints;
33+
private final Map<String, String> removeTaints;
34+
35+
KubectlTaint(ApiClient apiClient) {
36+
super(apiClient, V1Node.class);
37+
this.addingTaints = new HashMap<>();
38+
this.removeTaints = new HashMap<>();
39+
}
40+
41+
public KubectlTaint addTaint(String key, String effect) {
42+
return addTaint(key, null, effect);
43+
}
44+
45+
public KubectlTaint addTaint(String key, String value, String effect) {
46+
addingTaints.put(key, new ImmutablePair<>(value, effect));
47+
return this;
48+
}
49+
50+
public KubectlTaint removeTaint(String key) {
51+
removeTaints.put(key, null);
52+
return this;
53+
}
54+
55+
public KubectlTaint removeTaint(String key, String effect) {
56+
removeTaints.put(key, effect);
57+
return this;
58+
}
59+
60+
@Override
61+
public V1Node execute() throws KubectlException {
62+
verifyArguments();
63+
try {
64+
return executeInternal();
65+
} catch (ApiException | IOException ex) {
66+
throw new KubectlException(ex);
67+
}
68+
}
69+
70+
private V1Node executeInternal() throws KubectlException, ApiException, IOException {
71+
CoreV1Api v1 = new CoreV1Api(apiClient);
72+
V1Node node = v1.readNode(name, null, null, null);
73+
74+
TaintsBuilder builder = Taints.taints(node);
75+
for (Map.Entry<String, Pair<String, String>> taint : addingTaints.entrySet()) {
76+
builder.addTaint(
77+
taint.getKey(), taint.getValue().getLeft(), makeEffect(taint.getValue().getRight()));
78+
}
79+
for (Map.Entry<String, String> taint : removeTaints.entrySet()) {
80+
if (taint.getValue() == null) {
81+
builder.removeTaint(taint.getKey());
82+
} else {
83+
builder.removeTaint(taint.getKey(), makeEffect(taint.getValue()));
84+
}
85+
}
86+
return v1.replaceNode(name, node, null, null, null);
87+
}
88+
89+
private Effect makeEffect(String effect) throws KubectlException {
90+
if (effect.equals(Effect.NO_SCHEDULE.toString())) {
91+
return Effect.NO_SCHEDULE;
92+
}
93+
if (effect.equals(Effect.PREFER_NO_SCHEDULE.toString())) {
94+
return Effect.PREFER_NO_SCHEDULE;
95+
}
96+
if (effect.equals(Effect.NO_EXECUTE.toString())) {
97+
return Effect.NO_EXECUTE;
98+
}
99+
throw new KubectlException("Unknown effect: " + effect);
100+
}
101+
102+
private void verifyArguments() throws KubectlException {
103+
if (null == name) {
104+
throw new KubectlException("missing name argument");
105+
}
106+
}
107+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util.taints;
14+
15+
import io.kubernetes.client.openapi.models.V1Node;
16+
import io.kubernetes.client.openapi.models.V1NodeSpec;
17+
import io.kubernetes.client.openapi.models.V1Taint;
18+
import java.util.Iterator;
19+
20+
public class Taints {
21+
public static enum Effect {
22+
NO_SCHEDULE {
23+
public String toString() {
24+
return "NoSchedule";
25+
}
26+
},
27+
PREFER_NO_SCHEDULE {
28+
public String toString() {
29+
return "PreferNoSchedule";
30+
}
31+
},
32+
NO_EXECUTE {
33+
public String toString() {
34+
return "NoExcute";
35+
}
36+
}
37+
};
38+
39+
public static V1Taint findTaint(V1Node node, String key, Effect effect) {
40+
for (V1Taint taint : node.getSpec().getTaints()) {
41+
if (taint.getKey().equals(key) && taint.getEffect().equals(effect.toString())) {
42+
return taint;
43+
}
44+
}
45+
return null;
46+
}
47+
48+
public static TaintsBuilder taints(V1Node node) {
49+
return new TaintsBuilder(node);
50+
}
51+
52+
public static class TaintsBuilder {
53+
private V1Node node;
54+
55+
TaintsBuilder(V1Node node) {
56+
this.node = node;
57+
}
58+
59+
/**
60+
* Add a taint to a node
61+
*
62+
* @param key The key for the taint
63+
* @param effect The effect
64+
*/
65+
public TaintsBuilder addTaint(String key, Effect effect) {
66+
return addTaint(key, null, effect);
67+
}
68+
69+
/**
70+
* Add a taint to a node
71+
*
72+
* @param key The key for the taint
73+
* @param effect The effect
74+
*/
75+
public TaintsBuilder addTaint(String key, String value, Effect effect) {
76+
V1Taint taint = new V1Taint();
77+
taint.setKey(key);
78+
taint.setValue(value);
79+
taint.setEffect(effect.toString());
80+
if (node.getSpec() == null) {
81+
node.setSpec(new V1NodeSpec());
82+
}
83+
node.getSpec().addTaintsItem(taint);
84+
return this;
85+
}
86+
87+
/**
88+
* Add a taint to a node
89+
*
90+
* @param key The key for the taint
91+
* @param effect The effect
92+
*/
93+
public TaintsBuilder updateTaint(String key, String value, Effect effect) {
94+
V1Taint taint = findTaint(node, key, effect);
95+
taint.setValue(value);
96+
return this;
97+
}
98+
99+
/**
100+
* Remove all taints matching a specified key
101+
*
102+
* @param The key for the taint(s) to remove
103+
*/
104+
public TaintsBuilder removeTaint(String key) {
105+
if (node.getSpec() == null) {
106+
return this;
107+
}
108+
Iterator<V1Taint> taints = node.getSpec().getTaints().iterator();
109+
while (taints.hasNext()) {
110+
V1Taint taint = taints.next();
111+
if (taint.getKey().equals(key)) {
112+
taints.remove();
113+
}
114+
}
115+
return this;
116+
}
117+
118+
/**
119+
* Remove a taint matching a key and an effect
120+
*
121+
* @param key They key to match
122+
* @param effect The effect to match
123+
*/
124+
public TaintsBuilder removeTaint(String key, Effect effect) {
125+
if (node.getSpec() == null) {
126+
return this;
127+
}
128+
Iterator<V1Taint> taints = node.getSpec().getTaints().iterator();
129+
while (taints.hasNext()) {
130+
V1Taint taint = taints.next();
131+
if (taint.getKey().equals(key) && taint.getEffect().equals(effect.toString())) {
132+
taints.remove();
133+
}
134+
}
135+
return this;
136+
}
137+
}
138+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util.taints;
14+
15+
import static io.kubernetes.client.util.taints.Taints.taints;
16+
import static org.junit.Assert.assertNotNull;
17+
import static org.junit.Assert.assertNull;
18+
19+
import io.kubernetes.client.openapi.models.V1Node;
20+
import org.junit.Test;
21+
22+
public class TaintsTest {
23+
@Test
24+
public void testAddTaints() {
25+
V1Node node = new V1Node();
26+
taints(node)
27+
.addTaint("key1", Taints.Effect.NO_SCHEDULE)
28+
.addTaint("key2", Taints.Effect.PREFER_NO_SCHEDULE);
29+
30+
assertNotNull(Taints.findTaint(node, "key1", Taints.Effect.NO_SCHEDULE));
31+
assertNotNull(Taints.findTaint(node, "key2", Taints.Effect.PREFER_NO_SCHEDULE));
32+
}
33+
34+
@Test
35+
public void testRemoveTaints() {
36+
V1Node node = new V1Node();
37+
taints(node)
38+
.addTaint("key1", Taints.Effect.NO_SCHEDULE)
39+
.addTaint("key1", Taints.Effect.PREFER_NO_SCHEDULE)
40+
.addTaint("key1", Taints.Effect.NO_EXECUTE)
41+
.addTaint("key2", Taints.Effect.PREFER_NO_SCHEDULE)
42+
.addTaint("key3", Taints.Effect.NO_SCHEDULE)
43+
.addTaint("key3", Taints.Effect.NO_EXECUTE);
44+
45+
taints(node)
46+
// Remove all
47+
.removeTaint("key1")
48+
// Shouldn't remove, 'effect' is different.
49+
.removeTaint("key2", Taints.Effect.NO_SCHEDULE)
50+
// Remove matching
51+
.removeTaint("key3", Taints.Effect.NO_EXECUTE);
52+
53+
assertNull(Taints.findTaint(node, "key1", Taints.Effect.NO_SCHEDULE));
54+
assertNull(Taints.findTaint(node, "key1", Taints.Effect.PREFER_NO_SCHEDULE));
55+
assertNull(Taints.findTaint(node, "key1", Taints.Effect.NO_EXECUTE));
56+
assertNull(Taints.findTaint(node, "key3", Taints.Effect.NO_EXECUTE));
57+
assertNotNull(Taints.findTaint(node, "key2", Taints.Effect.PREFER_NO_SCHEDULE));
58+
assertNotNull(Taints.findTaint(node, "key3", Taints.Effect.NO_SCHEDULE));
59+
}
60+
}

0 commit comments

Comments
 (0)