forked from aliyun/terraform-provider-alicloud
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resource_alicloud_route_entry.go
216 lines (189 loc) · 6.88 KB
/
resource_alicloud_route_entry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package alicloud
import (
"fmt"
"strings"
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-alicloud/alicloud/connectivity"
)
func resourceAliyunRouteEntry() *schema.Resource {
return &schema.Resource{
Create: resourceAliyunRouteEntryCreate,
Read: resourceAliyunRouteEntryRead,
Delete: resourceAliyunRouteEntryDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"router_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Deprecated: "Attribute router_id has been deprecated and suggest removing it from your template.",
},
"route_table_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"destination_cidrblock": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"nexthop_type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"nexthop_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceAliyunRouteEntryCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
vpcService := VpcService{client}
rtId := d.Get("route_table_id").(string)
cidr := d.Get("destination_cidrblock").(string)
nt := d.Get("nexthop_type").(string)
ni := d.Get("nexthop_id").(string)
table, err := vpcService.QueryRouteTableById(rtId)
if err != nil {
if NotFoundError(err) {
d.SetId("")
return nil
}
return WrapError(err)
}
request := vpc.CreateCreateRouteEntryRequest()
request.RouteTableId = rtId
request.DestinationCidrBlock = cidr
request.NextHopType = nt
request.NextHopId = ni
// retry 10 min to create lots of entries concurrently
err = resource.Retry(10*time.Minute, func() *resource.RetryError {
if err := vpcService.WaitForAllRouteEntries(rtId, Available, DefaultTimeout); err != nil {
return resource.NonRetryableError(fmt.Errorf("WaitFor route entries got error: %#v", err))
}
// Update token every time when request is change.
// Token is used for idempotency check, and each request needs to be updated.
// The system will return last result whatever the last request is success or not.
request.ClientToken = buildClientToken(request.GetActionName())
args := *request
_, err := client.WithVpcClient(func(vpcClient *vpc.Client) (interface{}, error) {
return vpcClient.CreateRouteEntry(&args)
})
if err != nil {
// Route Entry does not support concurrence when creating or deleting it;
// Route Entry does not support creating or deleting within 5 seconds frequently
// It must ensure all the route entries and vswitches' status must be available before creating or deleting route entry.
if IsExceptedErrors(err, []string{TaskConflict, IncorrectRouteEntryStatus, Throttling}) {
return resource.RetryableError(fmt.Errorf("Create route entry timeout and got an error: %#v", err))
}
if IsExceptedError(err, RouterEntryConflictDuplicated) {
en, err := vpcService.QueryRouteEntry(rtId, cidr, nt, ni)
if err != nil {
return resource.NonRetryableError(err)
}
return resource.NonRetryableError(fmt.Errorf("The route entry %s has already existed. "+
"Please import it using ID '%s:%s:%s:%s:%s' or specify a new 'destination_cidrblock' and try again.",
en.DestinationCidrBlock, en.RouteTableId, table.VRouterId, en.DestinationCidrBlock, en.NextHopType, ni))
}
return resource.NonRetryableError(fmt.Errorf("Creating Route entry got an error: %#v", err))
}
return nil
})
if err != nil {
return fmt.Errorf("Create Vroute Entry got an error :%#v", err)
}
// route_table_id:router_id:destination_cidrblock:nexthop_type:nexthop_id
d.SetId(rtId + ":" + table.VRouterId + ":" + cidr + ":" + nt + ":" + ni)
if err := vpcService.WaitForAllRouteEntries(rtId, Available, DefaultTimeout); err != nil {
return fmt.Errorf("WaitFor route entry got error: %#v", err)
}
return resourceAliyunRouteEntryRead(d, meta)
}
func resourceAliyunRouteEntryRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
vpcService := VpcService{client}
parts := strings.Split(d.Id(), ":")
rtId := parts[0]
rId := parts[1]
cidr := parts[2]
nexthop_type := parts[3]
nexthop_id := parts[4]
en, err := vpcService.QueryRouteEntry(rtId, cidr, nexthop_type, nexthop_id)
if err != nil {
if NotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error route entry: %#v", err)
}
d.Set("router_id", rId)
d.Set("route_table_id", en.RouteTableId)
d.Set("destination_cidrblock", en.DestinationCidrBlock)
d.Set("nexthop_type", en.NextHopType)
d.Set("nexthop_id", en.InstanceId)
return nil
}
func resourceAliyunRouteEntryDelete(d *schema.ResourceData, meta interface{}) error {
args, err := buildAliyunRouteEntryDeleteArgs(d, meta)
if err != nil {
return err
}
client := meta.(*connectivity.AliyunClient)
vpcService := VpcService{client}
parts := strings.Split(d.Id(), ":")
rtId := parts[0]
cidr := parts[2]
nexthop_type := parts[3]
nexthop_id := parts[4]
retryTimes := 7
return resource.Retry(10*time.Minute, func() *resource.RetryError {
en, err := vpcService.QueryRouteEntry(rtId, cidr, nexthop_type, nexthop_id)
if err != nil {
if NotFoundError(err) {
return nil
}
return resource.NonRetryableError(fmt.Errorf("Error route entry: %#v", err))
}
if en.Status != string(Available) {
return resource.RetryableError(fmt.Errorf("Delete route entry timeout and got an error: %#v.", err))
}
_, err = client.WithVpcClient(func(vpcClient *vpc.Client) (interface{}, error) {
return vpcClient.DeleteRouteEntry(args)
})
if err != nil {
if IsExceptedErrors(err, []string{InvalidRouteEntryNotFound}) {
return nil
}
if IsExceptedErrors(err, []string{IncorrectVpcStatus, TaskConflict, IncorrectRouteEntryStatus, RouterEntryForbbiden, UnknownError}) {
// Route Entry does not support creating or deleting within 5 seconds frequently
time.Sleep(time.Duration(retryTimes) * time.Second)
retryTimes += 7
return resource.RetryableError(fmt.Errorf("Delete route entry timeout and got an error: %#v.", err))
}
return resource.NonRetryableError(fmt.Errorf("Deleting RouteEntry got an error: %#v", err))
}
return nil
})
}
func buildAliyunRouteEntryDeleteArgs(d *schema.ResourceData, meta interface{}) (*vpc.DeleteRouteEntryRequest, error) {
request := vpc.CreateDeleteRouteEntryRequest()
request.RouteTableId = d.Get("route_table_id").(string)
request.DestinationCidrBlock = d.Get("destination_cidrblock").(string)
if v := d.Get("destination_cidrblock").(string); v != "" {
request.DestinationCidrBlock = v
}
if v := d.Get("nexthop_id").(string); v != "" {
request.NextHopId = v
}
return request, nil
}