@@ -6,8 +6,6 @@ package kubernetes
66import (
77 "context"
88 "fmt"
9- "log"
10- "strings"
119
1210 "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1311 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -17,8 +15,6 @@ import (
1715 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1816 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1917 "k8s.io/apimachinery/pkg/types"
20- "k8s.io/kubernetes/pkg/apis/core/helper"
21- "k8s.io/kubernetes/pkg/util/taints"
2218)
2319
2420var taintMap = map [string ]v1.TaintEffect {
@@ -33,6 +29,24 @@ func resourceKubernetesNodeTaint() *schema.Resource {
3329 ReadContext : resourceKubernetesNodeTaintRead ,
3430 UpdateContext : resourceKubernetesNodeTaintUpdate ,
3531 DeleteContext : resourceKubernetesNodeTaintDelete ,
32+ CustomizeDiff : func (ctx context.Context , rd * schema.ResourceDiff , i interface {}) error {
33+ if ! rd .HasChange ("taint" ) {
34+ return nil
35+ }
36+ // check for duplicate taint keys
37+ taintkeys := map [string ]int {}
38+ for _ , t := range rd .Get ("taint" ).([]interface {}) {
39+ taint := t .(map [string ]interface {})
40+ key := taint ["key" ].(string )
41+ taintkeys [key ] = taintkeys [key ] + 1
42+ }
43+ for k , v := range taintkeys {
44+ if v > 1 {
45+ return fmt .Errorf ("taint: duplicate taint key %q: taint keys must be unique" , k )
46+ }
47+ }
48+ return nil
49+ },
3650 Schema : map [string ]* schema.Schema {
3751 "metadata" : {
3852 Type : schema .TypeList ,
@@ -64,7 +78,6 @@ func resourceKubernetesNodeTaint() *schema.Resource {
6478 "taint" : {
6579 Type : schema .TypeList ,
6680 Required : true ,
67- MaxItems : 1 ,
6881 Elem : & schema.Resource {
6982 Schema : nodeTaintFields (),
7083 },
@@ -89,10 +102,8 @@ func resourceKubernetesNodeTaintDelete(ctx context.Context, d *schema.ResourceDa
89102}
90103
91104func resourceKubernetesNodeTaintRead (ctx context.Context , d * schema.ResourceData , m interface {}) diag.Diagnostics {
92- nodeName , idTaint , err := idToNodeTaint (d .Id ())
93- if err != nil {
94- return diag .FromErr (err )
95- }
105+ meta := expandMetadata (d .Get ("metadata" ).([]interface {}))
106+ nodeName := meta .Name
96107
97108 conn , err := m .(KubeClientsets ).MainClientset ()
98109 if err != nil {
@@ -109,11 +120,8 @@ func resourceKubernetesNodeTaintRead(ctx context.Context, d *schema.ResourceData
109120 d .SetId ("" )
110121 return nil
111122 }
112- if ! hasTaint (nodeTaints , idTaint ) {
113- d .SetId ("" )
114- return nil
115- }
116- d .Set ("taint" , flattenNodeTaints (* idTaint ))
123+
124+ d .Set ("taint" , flattenNodeTaints (nodeTaints ... ))
117125 return nil
118126}
119127
@@ -127,7 +135,7 @@ func resourceKubernetesNodeTaintUpdate(ctx context.Context, d *schema.ResourceDa
127135 }
128136 nodeApi := conn .CoreV1 ().Nodes ()
129137
130- node , err : = nodeApi .Get (ctx , nodeName , metav1.GetOptions {})
138+ _ , err = nodeApi .Get (ctx , nodeName , metav1.GetOptions {})
131139 if err != nil {
132140 if d .Id () == "" {
133141 if statusErr , ok := err .(* errors.StatusError ); ok && errors .IsNotFound (statusErr ) {
@@ -139,28 +147,9 @@ func resourceKubernetesNodeTaintUpdate(ctx context.Context, d *schema.ResourceDa
139147 }
140148
141149 taints := d .Get ("taint" ).([]interface {})
142- newTaint , err := expandNodeTaint (taints [0 ].(map [string ]interface {}))
143- if err != nil {
144- return diag .FromErr (err )
145- }
146- var newNode * v1.Node
147150 if d .Id () == "" {
148- var removed bool
149- newNode , removed = removeTaint (node , newTaint )
150- if ! removed {
151- return diag.Diagnostics {{
152- Severity : diag .Warning ,
153- Summary : "Resource deleted" ,
154- Detail : fmt .Sprintf ("Node %s does not have taint %+v. You should re-create it, or remove this resource from your configuration" , nodeName , newTaint ),
155- }}
156- }
157- } else {
158- log .Printf ("[INFO] adding taint %+v to node %s" , newTaint , nodeName )
159- var updated bool
160- newNode , updated = addOrUpdateTaint (node , newTaint )
161- if ! updated {
162- return diag .Errorf ("Node %s already has taint %+v" , nodeName , newTaint )
163- }
151+ // make taints an empty list if we're deleting the resource
152+ taints = []interface {}{}
164153 }
165154 patchObj := map [string ]interface {}{
166155 "apiVersion" : "v1" ,
@@ -169,7 +158,7 @@ func resourceKubernetesNodeTaintUpdate(ctx context.Context, d *schema.ResourceDa
169158 "name" : nodeName ,
170159 },
171160 "spec" : map [string ]interface {}{
172- "taints" : flattenNodeTaints ( newNode . Spec . Taints ... ) ,
161+ "taints" : taints ,
173162 },
174163 }
175164 patch := unstructured.Unstructured {
@@ -197,93 +186,14 @@ func resourceKubernetesNodeTaintUpdate(ctx context.Context, d *schema.ResourceDa
197186 if d .Id () == "" {
198187 return nil
199188 }
200- d .SetId (nodeTaintToId (nodeName , taints ))
201189 return resourceKubernetesNodeTaintRead (ctx , d , m )
202190}
203191
204- func addOrUpdateTaint (node * v1.Node , taint * v1.Taint ) (* v1.Node , bool ) {
205- nodeTaints := node .Spec .Taints
206- newTaints := []v1.Taint {}
207- updated := false
208- for i := range nodeTaints {
209- log .Printf ("[INFO] Checking taint: %+v" , nodeTaints [i ])
210- if taint .MatchTaint (& nodeTaints [i ]) {
211- if helper .Semantic .DeepEqual (* taint , nodeTaints [i ]) {
212- return node , false
213- }
214- newTaints = append (newTaints , * taint )
215- updated = true
216- continue
217- }
218- newTaints = append (newTaints , nodeTaints [i ])
219- }
220- if ! updated {
221- newTaints = append (newTaints , * taint )
222- log .Printf ("[INFO] appended taint: %+v" , taint )
223- updated = true
224- }
225- newNode := node .DeepCopy ()
226- newNode .Spec .Taints = newTaints
227- return newNode , updated
228- }
229-
230- func removeTaint (node * v1.Node , delTaint * v1.Taint ) (* v1.Node , bool ) {
231- taints := node .Spec .Taints
232- newTaints := []v1.Taint {}
233- deleted := false
234- for i := range taints {
235- if delTaint .MatchTaint (& taints [i ]) {
236- deleted = true
237- continue
238- }
239- newTaints = append (newTaints , taints [i ])
240- }
241- if ! deleted {
242- return node , false
243- }
244- newNode := node .DeepCopy ()
245- newNode .Spec .Taints = newTaints
246- return newNode , deleted
247- }
248-
249- func hasTaint (taints []v1.Taint , taint * v1.Taint ) bool {
250- for i := range taints {
251- if taint .MatchTaint (& taints [i ]) {
252- return true
253- }
254- }
255- return false
256- }
257-
258- func expandNodeTaint (t map [string ]interface {}) (* v1.Taint , error ) {
259- tt := expandStringMap (t )
260- taintEffect , ok := taintMap [tt ["effect" ]]
261- if ! ok {
262- return nil , fmt .Errorf ("Invalid taint effect '%s'" , tt ["effect" ])
263- }
264- taint := & v1.Taint {
265- Key : tt ["key" ],
266- Value : tt ["value" ],
267- Effect : taintEffect ,
268- }
269- return taint , nil
270- }
271-
272192func nodeTaintToId (nodeName string , taints []interface {}) string {
273- t := taints [0 ].(map [string ]interface {})
274- return fmt .Sprintf ("%s,%s=%s:%s" , nodeName , t ["key" ], t ["value" ], t ["effect" ])
275- }
276-
277- func idToNodeTaint (id string ) (string , * v1.Taint , error ) {
278- idVals := strings .Split (id , "," )
279- nodeName := idVals [0 ]
280- taintStr := idVals [1 ]
281- taints , _ , err := taints .ParseTaints ([]string {taintStr })
282- if err != nil {
283- return "" , nil , err
284- }
285- if len (taints ) == 0 {
286- return "" , nil , fmt .Errorf ("failed to parse taint %s" , taintStr )
193+ var id string = fmt .Sprintf ("%s" , nodeName )
194+ for _ , t := range taints {
195+ taint := t .(map [string ]interface {})
196+ id += fmt .Sprintf (",%s=%s:%s" , taint ["key" ], taint ["value" ], taint ["effect" ])
287197 }
288- return nodeName , & taints [ 0 ], nil
198+ return id
289199}
0 commit comments