@@ -22,6 +22,7 @@ package openstack
22
22
import (
23
23
"context"
24
24
"fmt"
25
+ "net"
25
26
"net/url"
26
27
"os"
27
28
"os/exec"
@@ -43,14 +44,6 @@ const (
43
44
configDriveUserdataPath = "/openstack/latest/user_data"
44
45
)
45
46
46
- var (
47
- metadataServiceUrl = url.URL {
48
- Scheme : "http" ,
49
- Host : "169.254.169.254" ,
50
- Path : "openstack/latest/user_data" ,
51
- }
52
- )
53
-
54
47
func init () {
55
48
platform .Register (platform.Provider {
56
49
Name : "openstack" ,
@@ -166,14 +159,86 @@ func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, path string)
166
159
return os .ReadFile (filepath .Join (mnt , configDriveUserdataPath ))
167
160
}
168
161
162
+ func FindIPv6InterfaceName () (string , error ) {
163
+ interfaces , err := net .Interfaces ()
164
+ if err != nil {
165
+ return "" , err
166
+ }
167
+
168
+ for _ , iface := range interfaces {
169
+ // Check if the interface is up and not loopback
170
+ if iface .Flags & net .FlagUp != 0 && iface .Flags & net .FlagLoopback == 0 {
171
+ addrs , err := iface .Addrs ()
172
+ if err != nil {
173
+ continue
174
+ }
175
+
176
+ for _ , addr := range addrs {
177
+ // Check if the address is an IPv6
178
+ if ipnet , ok := addr .(* net.IPNet ); ok && ipnet .IP .To4 () == nil && ! ipnet .IP .IsLinkLocalUnicast () {
179
+ return iface .Name , nil
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ return "" , fmt .Errorf ("no IPv6 interface name found" )
186
+ }
187
+
169
188
func fetchConfigFromMetadataService (f * resource.Fetcher ) ([]byte , error ) {
170
- res , err := f .FetchToBuffer (metadataServiceUrl , resource.FetchOptions {})
171
189
172
- // the metadata server exists but doesn't contain any actual metadata,
173
- // assume that there is no config specified
174
- if err == resource .ErrNotFound {
175
- return nil , nil
190
+ // Find IPv6 name
191
+ iface , err := FindIPv6InterfaceName ()
192
+ if err != nil {
193
+ return nil , err
194
+ }
195
+
196
+ // Construct URL
197
+ var (
198
+ ipv4MetadataServiceUrl = url.URL {
199
+ Scheme : "http" ,
200
+ Host : "169.254.169.254" ,
201
+ Path : "openstack/latest/user_data" ,
202
+ }
203
+ ipv6MetadataServiceUrl = url.URL {
204
+ Scheme : "http" ,
205
+ Host : fmt .Sprintf ("[fe80::a9fe:a9fe%%%s]" , url .PathEscape (iface )),
206
+ Path : "openstack/latest/user_data" ,
207
+ }
208
+ )
209
+
210
+ var resIPv4 , resIPv6 []byte
211
+ var errIPv4 , errIPv6 error
212
+
213
+ // Try IPv4 endpoint
214
+ resIPv4 , errIPv4 = f .FetchToBuffer (ipv4MetadataServiceUrl , resource.FetchOptions {})
215
+ if errIPv4 != nil && errIPv4 != resource .ErrNotFound {
216
+ f .Logger .Err ("Failed to fetch config from IPv4: %v" , errIPv4 )
217
+ }
218
+
219
+ // Try IPv6 endpoint
220
+ resIPv6 , errIPv6 = f .FetchToBuffer (ipv6MetadataServiceUrl , resource.FetchOptions {})
221
+ if errIPv6 != nil && errIPv6 != resource .ErrNotFound {
222
+ f .Logger .Err ("Failed to fetch config from IPv6: %v" , errIPv6 )
223
+ }
224
+
225
+ // If both IPv4 and IPv6 have valid data, combine them
226
+ if resIPv4 != nil && resIPv6 != nil {
227
+ return append (resIPv4 , resIPv6 ... ), nil
228
+ } else if resIPv4 != nil {
229
+ return resIPv4 , nil
230
+ } else if resIPv6 != nil {
231
+ return resIPv6 , nil
232
+ }
233
+
234
+ // If both endpoints fail, return the appropriate error
235
+ if errIPv4 != nil {
236
+ return nil , errIPv4
237
+ }
238
+ if errIPv6 != nil {
239
+ return nil , errIPv6
176
240
}
177
241
178
- return res , err
242
+ // If both endpoints return ErrNotFound
243
+ return nil , nil
179
244
}
0 commit comments