Skip to content

Commit 55bfbab

Browse files
belgaied2bigkevmcd
authored andcommitted
feat: Added a resource cluster_proxy_config_v2
This allows enabling JWT tokens on downstream clusters. Co-authored-by: Mohamed Belgaied <[email protected]> Signed-off-by: Kevin McDermott <[email protected]>
1 parent 1c0e84c commit 55bfbab

9 files changed

+645
-0
lines changed

rancher2/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,19 @@ func (c *Config) WaitForClusterState(clusterID, state string, interval time.Dura
727727
}
728728
}
729729

730+
// getObjectV2ByID uses the Steve API to get a resource by ID.
731+
//
732+
// The clusterID must be the cluster to access the resource from e.g. "local" or
733+
// "c-wt9cd".
734+
// For remote clusters, this will be queried using the cluster-proxy.
735+
//
736+
// The id must be in namespace/name format e.g. "local/my-resource".
737+
//
738+
// The APIType must be fully qualified e.g. "configmap" or
739+
// "rke-machine-config.cattle.io.amazonec2config".
740+
//
741+
// The response is stored in resp which should be a pointer to a struct for
742+
// receiving the resource.
730743
func (c *Config) getObjectV2ByID(clusterID, id, APIType string, resp interface{}) error {
731744
if id == "" {
732745
return fmt.Errorf("Object V2 id is nil")
@@ -785,6 +798,7 @@ func (c *Config) createObjectV2(clusterID string, APIType string, obj, resp inte
785798
if err != nil {
786799
return err
787800
}
801+
788802
return client.Create(APIType, obj, resp)
789803
}
790804

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package rancher2
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
5+
)
6+
7+
func dataSourceRancher2ClusterProxyConfig() *schema.Resource {
8+
return &schema.Resource{
9+
Read: resourceRancher2ClusterProxyConfigV2Read,
10+
Schema: map[string]*schema.Schema{
11+
"cluster_id": {
12+
Computed: true,
13+
Type: schema.TypeString,
14+
},
15+
"enabled": {
16+
Computed: true,
17+
Type: schema.TypeBool,
18+
},
19+
},
20+
}
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package rancher2
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
5+
)
6+
7+
const (
8+
clusterProxyConfigV2Name = "clusterproxyconfig"
9+
)
10+
11+
func resourceRancher2ClusterProxyConfigV2Import(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
12+
err := resourceRancher2ClusterProxyConfigV2Read(d, meta)
13+
if err != nil || d.Id() == "" {
14+
return nil, err
15+
}
16+
17+
return []*schema.ResourceData{d}, nil
18+
}

rancher2/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func Provider() terraform.ResourceProvider {
125125
"rancher2_cluster": resourceRancher2Cluster(),
126126
"rancher2_cluster_v2": resourceRancher2ClusterV2(),
127127
"rancher2_cluster_driver": resourceRancher2ClusterDriver(),
128+
"rancher2_cluster_proxy_config_v2": resourceRancher2ClusterProxyConfigV2(),
128129
"rancher2_cluster_role_template_binding": resourceRancher2ClusterRoleTemplateBinding(),
129130
"rancher2_cluster_sync": resourceRancher2ClusterSync(),
130131
"rancher2_cluster_template": resourceRancher2ClusterTemplate(),
@@ -180,6 +181,7 @@ func Provider() terraform.ResourceProvider {
180181
"rancher2_setting": dataSourceRancher2Setting(),
181182
"rancher2_storage_class_v2": dataSourceRancher2StorageClassV2(),
182183
"rancher2_user": dataSourceRancher2User(),
184+
"rancher2_cluster_proxy_config_v2": dataSourceRancher2ClusterProxyConfig(),
183185
},
184186

185187
ConfigureFunc: providerConfigure,
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
package rancher2
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"time"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
11+
"github.com/rancher/norman/types"
12+
)
13+
14+
func resourceRancher2ClusterProxyConfigV2() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceRancher2ClusterProxyConfigV2Create,
17+
Read: resourceRancher2ClusterProxyConfigV2Read,
18+
Delete: resourceRancher2ClusterProxyConfigV2Delete,
19+
Update: resourceRancher2ClusterProxyConfigV2Update,
20+
Importer: &schema.ResourceImporter{
21+
State: resourceRancher2ClusterProxyConfigV2Import,
22+
},
23+
Schema: clusterProxyConfigV2Fields(),
24+
Timeouts: &schema.ResourceTimeout{
25+
Create: schema.DefaultTimeout(10 * time.Minute),
26+
Update: schema.DefaultTimeout(10 * time.Minute),
27+
Delete: schema.DefaultTimeout(10 * time.Minute),
28+
},
29+
}
30+
}
31+
32+
func resourceRancher2ClusterProxyConfigV2Create(d *schema.ResourceData, meta interface{}) error {
33+
clusterID := d.Get("cluster_id").(string)
34+
35+
log.Printf("[INFO] Creating ClusterProxyConfig for cluster %s", clusterID)
36+
37+
clusterProxyConfigV2, err := expandClusterProxyConfigV2(d)
38+
if err != nil {
39+
return err
40+
}
41+
42+
newClusterProxyConfigV2, err := createClusterProxyConfigV2(meta.(*Config), clusterID, clusterProxyConfigV2)
43+
if err != nil {
44+
return err
45+
}
46+
47+
d.SetId(clusterID + "/" + clusterProxyConfigV2Name)
48+
49+
stateConf := &resource.StateChangeConf{
50+
Pending: []string{"creating"},
51+
Target: []string{"active"},
52+
Refresh: clusterProxyConfigV2StateRefreshFunc(meta.(*Config), clusterID),
53+
Timeout: d.Timeout(schema.TimeoutCreate),
54+
Delay: 1 * time.Second,
55+
MinTimeout: 3 * time.Second,
56+
}
57+
res, waitErr := stateConf.WaitForState()
58+
if waitErr != nil {
59+
return fmt.Errorf("waiting for cluster proxy config (%s) to be active: %s", newClusterProxyConfigV2.ID, waitErr)
60+
}
61+
log.Printf("[DEBUG] resourceRancher2ClusterProxyConfigV2Create: %#v", res)
62+
63+
return resourceRancher2ClusterProxyConfigV2Read(d, meta)
64+
}
65+
66+
func resourceRancher2ClusterProxyConfigV2Read(d *schema.ResourceData, meta interface{}) error {
67+
clusterID := d.Get("cluster_id").(string)
68+
69+
log.Printf("[INFO] Refreshing ClusterProxyConfig for cluster %s", clusterID)
70+
71+
config := meta.(*Config)
72+
clusterProxyConfigV2Id := clusterID + "/" + clusterProxyConfigV2Name
73+
resp := &ClusterProxyConfigV2{}
74+
err := config.getObjectV2ByID(rancher2DefaultLocalClusterID, clusterProxyConfigV2Id, clusterProxyConfigV2ApiType, resp)
75+
if err != nil {
76+
if IsNotFound(err) || IsNotAccessibleByID(err) {
77+
log.Printf("[INFO] Cluster V2 %s not found", d.Id())
78+
d.SetId("")
79+
return nil
80+
}
81+
return err
82+
}
83+
return flattenClusterProxyConfigV2(d, resp)
84+
}
85+
86+
func resourceRancher2ClusterProxyConfigV2Delete(d *schema.ResourceData, meta interface{}) error {
87+
clusterID := d.Get("cluster_id").(string)
88+
log.Printf("[INFO] Deleting ClusterProxyConfig for cluster %s", clusterID)
89+
90+
clusterProxyConfigV2Id := clusterID + "/" + clusterProxyConfigV2Name
91+
// First, get the current object to have the resource info for deletion
92+
obj := &ClusterProxyConfigV2{}
93+
err := meta.(*Config).getObjectV2ByID(rancher2DefaultLocalClusterID, clusterProxyConfigV2Id, clusterProxyConfigV2ApiType, obj)
94+
if err != nil {
95+
if IsNotFound(err) {
96+
d.SetId("")
97+
return nil
98+
}
99+
return err
100+
}
101+
102+
err = deleteClusterProxyConfigV2(meta.(*Config), obj)
103+
if err != nil {
104+
return err
105+
}
106+
107+
stateConf := &resource.StateChangeConf{
108+
Pending: []string{"removing"},
109+
Target: []string{"removed"},
110+
Refresh: clusterProxyConfigV2StateRefreshFunc(meta.(*Config), clusterID),
111+
Timeout: d.Timeout(schema.TimeoutDelete),
112+
Delay: 1 * time.Second,
113+
MinTimeout: 3 * time.Second,
114+
}
115+
res, waitErr := stateConf.WaitForState()
116+
if waitErr != nil {
117+
return fmt.Errorf("waiting for cluster proxy config (%s) to be removed: %s", clusterProxyConfigV2Id, waitErr)
118+
}
119+
log.Printf("[DEBUG] resourceRancher2ClusterProxyConfigV2Delete: %#v", res)
120+
121+
d.SetId("")
122+
123+
return nil
124+
}
125+
126+
// clusterProxyConfigV2StateRefreshFunc returns a resource.StateRefreshFunc, used to watch a ClusterProxyConfig.
127+
func clusterProxyConfigV2StateRefreshFunc(c *Config, clusterID string) resource.StateRefreshFunc {
128+
return func() (interface{}, string, error) {
129+
clusterProxyConfigV2Id := clusterID + "/" + clusterProxyConfigV2Name
130+
obj := &ClusterProxyConfigV2{}
131+
err := c.getObjectV2ByID(rancher2DefaultLocalClusterID, clusterProxyConfigV2Id, clusterProxyConfigV2ApiType, obj)
132+
if err != nil {
133+
if IsNotFound(err) {
134+
return obj, "removed", nil
135+
}
136+
return nil, "", err
137+
}
138+
139+
return obj, "active", nil
140+
}
141+
}
142+
143+
func createClusterProxyConfigV2(c *Config, clusterID string, obj *ClusterProxyConfigV2) (*ClusterProxyConfigV2, error) {
144+
if c == nil {
145+
return nil, fmt.Errorf("creating ClusterProxyConfig V2: Provider config is nil")
146+
}
147+
if clusterID == "" {
148+
return nil, fmt.Errorf("creating ClusterProxyConfig V2: Cluster ID is empty")
149+
}
150+
if obj == nil {
151+
return nil, fmt.Errorf("creating ClusterProxyConfig V2: ClusterProxyConfig V2 is nil")
152+
}
153+
154+
resp := &ClusterProxyConfigV2{}
155+
156+
err := c.createObjectV2(rancher2DefaultLocalClusterID, clusterProxyConfigV2ApiType, obj, resp)
157+
if err != nil {
158+
return nil, fmt.Errorf("creating ClusterProxyConfig V2: %s", err)
159+
}
160+
161+
return resp, nil
162+
}
163+
164+
func resourceRancher2ClusterProxyConfigV2Update(d *schema.ResourceData, meta interface{}) error {
165+
clusterID := d.Get("cluster_id").(string)
166+
clusterProxyConfigV2Id := clusterID + "/" + clusterProxyConfigV2Name
167+
168+
log.Printf("[INFO] Updating ClusterProxyConfig for cluster %s", clusterID)
169+
170+
clusterProxyConfigV2, err := expandClusterProxyConfigV2(d)
171+
if err != nil {
172+
return err
173+
}
174+
175+
_, err = updateClusterProxyConfigV2(meta.(*Config), clusterProxyConfigV2Id, clusterProxyConfigV2)
176+
if err != nil {
177+
return err
178+
}
179+
180+
stateConf := &resource.StateChangeConf{
181+
Pending: []string{"updating"},
182+
Target: []string{"active"},
183+
Refresh: clusterProxyConfigV2StateRefreshFunc(meta.(*Config), clusterID),
184+
Timeout: d.Timeout(schema.TimeoutUpdate),
185+
Delay: 1 * time.Second,
186+
MinTimeout: 3 * time.Second,
187+
}
188+
res, waitErr := stateConf.WaitForState()
189+
if waitErr != nil {
190+
return fmt.Errorf("waiting for cluster proxy config (%s) to be updated: %s", clusterProxyConfigV2Id, waitErr)
191+
}
192+
log.Printf("[DEBUG] resourceRancher2ClusterProxyConfigV2Update: %#v", res)
193+
194+
return resourceRancher2ClusterProxyConfigV2Read(d, meta)
195+
}
196+
197+
func updateClusterProxyConfigV2(c *Config, id string, obj *ClusterProxyConfigV2) (*ClusterProxyConfigV2, error) {
198+
if c == nil {
199+
return nil, fmt.Errorf("updating ClusterProxyConfig V2: Provider config is nil")
200+
}
201+
202+
if id == "" {
203+
return nil, fmt.Errorf("updating ClusterProxyConfig V2: ID is empty")
204+
}
205+
206+
if obj == nil {
207+
return nil, fmt.Errorf("updating ClusterProxyConfig V2: ClusterProxyConfig V2 is nil")
208+
}
209+
210+
resp := &ClusterProxyConfigV2{}
211+
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
212+
defer cancel()
213+
214+
existingObj := &ClusterProxyConfigV2{}
215+
if err := c.getObjectV2ByID(rancher2DefaultLocalClusterID, id, clusterProxyConfigV2ApiType, existingObj); err != nil {
216+
return nil, err
217+
}
218+
obj.ObjectMeta.ResourceVersion = existingObj.ResourceVersion
219+
220+
for {
221+
err := c.updateObjectV2(rancher2DefaultLocalClusterID, id, clusterProxyConfigV2ApiType, obj, resp)
222+
if err == nil {
223+
return resp, err
224+
}
225+
226+
if !IsServerError(err) && !IsUnknownSchemaType(err) && !IsConflict(err) {
227+
return nil, err
228+
}
229+
230+
if IsConflict(err) {
231+
// Read object again and update ObjectMeta.ResourceVersion before retry
232+
newObj := &ClusterProxyConfigV2{}
233+
if err := c.getObjectV2ByID(rancher2DefaultLocalClusterID, id, clusterProxyConfigV2ApiType, newObj); err != nil {
234+
return nil, err
235+
}
236+
237+
obj.ObjectMeta.ResourceVersion = newObj.ObjectMeta.ResourceVersion
238+
}
239+
240+
select {
241+
case <-time.After(rancher2RetriesWait * time.Second):
242+
case <-ctx.Done():
243+
return nil, fmt.Errorf("timeout updating ClusterProxyConfig V2 ID %s: %v", id, err)
244+
}
245+
}
246+
}
247+
248+
func deleteClusterProxyConfigV2(c *Config, obj *ClusterProxyConfigV2) error {
249+
if c == nil {
250+
return fmt.Errorf("deleting ClusterProxyConfig V2: Provider config is nil")
251+
}
252+
if obj == nil {
253+
return fmt.Errorf("deleting ClusterProxyConfig V2: ClusterProxyConfig V2 is nil")
254+
}
255+
256+
resource := &types.Resource{
257+
ID: obj.ID,
258+
Type: clusterProxyConfigV2ApiType,
259+
Links: obj.Links,
260+
Actions: obj.Actions,
261+
}
262+
return c.deleteObjectV2(rancher2DefaultLocalClusterID, resource)
263+
}

0 commit comments

Comments
 (0)