-
Notifications
You must be signed in to change notification settings - Fork 253
OpenStack single-stack IPv6 support #1909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
9e7cba8
to
87e4a48
Compare
Not sure that will work. What happens if the instance is IPv6 only? Do requests fail differently than if the network is not up yet? |
One of the principle behind Ignition is that it will retry indefinitely until the server explicitly returns an error. |
87e4a48
to
5fad1fd
Compare
I updated my code logic. In an IPv6-only instance, IPv4 will fail because it's not found and the resources will be returned from IPv6. If both fails it will return errors. The error will be handled with a message in dispatch function above. It makes sense? One thing I need to consider is the possibility of having both IPv4 and IPv6 simultaneously. Is that possible? If yes, I need to handle this case as well. |
Nice. I guess this is being handled in
Thanks! |
Yes, this is very common. |
5fad1fd
to
91f89b5
Compare
I updated the code to cover this scenarios. So, now the code looks like this:
These should be the basic scenarios. Now, I'm working on running my code in the OpenStack environment and reading the necessary docs to test it correctly. Thanks for reviewing and helping me with this task! |
91f89b5
to
a850f29
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something to take into consideration:
Scheme: "http", | ||
Host: "169.254.169.254", | ||
Path: "openstack/latest/user_data", | ||
} | ||
ipv6MetadataServiceUrl = url.URL{ | ||
Scheme: "http", | ||
Host: "[fe80::a9fe:a9fe]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we might need to specify the interface name like it's mentioned on the docs
https://docs.openstack.org/nova/latest/user/metadata.html#the-metadata-service
and how it's done elsewhere
https://github.com/canonical/cloud-init/blob/main/cloudinit/sources/DataSourceOpenStack.py#L76
d81e5fb
to
6b8a11e
Compare
} | ||
ipv6MetadataServiceUrl = url.URL{ | ||
Scheme: "http", | ||
Host: fmt.Sprintf("[fe80::a9fe:a9fe%%%s]", url.PathEscape(iface)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need to change to [fe80::a9fe:a9fe%25%s]
refer to https://docs.openstack.org/nova/latest/user/metadata.html#the-metadata-service
f2979b8
to
fa50682
Compare
fa50682
to
ee0d2af
Compare
e105dea
to
1569e4c
Compare
1569e4c
to
35c1993
Compare
Scheme: "http", | ||
Host: "169.254.169.254", | ||
Path: "openstack/latest/user_data", | ||
} | ||
metadataServiceUrlIPv6 = url.URL{ | ||
Scheme: "http", | ||
Host: "fe80::a9fe:a9fe%", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried having the host be [fe90::a9fe:a9fe%]? since you are getting an error on fetch due to improper escaping ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey team, I'm experiencing an error while trying to connect the server. I added some log to the code, then I ran
Not sure why is not parsing correctly. |
[SOLVED] Mobbing session output: We utilized an OpenStack environment to fetch and curl the IPv6, but were unable to get a result. Here are the logs:
[EDIT] I was able to reproduce this commands in other machine and worked. It might be a older openstack version, since IPV6 support only came after Victoria release. |
787e7b9
to
0bb5233
Compare
18f03e7
to
b3d311d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really good start, and thank you for working on this, just a few questions and nits.
isIPv6 := ip.To4() == nil | ||
return isIPv6 | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to have some reference on why you are looking for the zone ID in the manner that you are. A simple link to the IPV6 metadata service docs should be good with a quick summary? wdyt?
return "", fmt.Errorf("no active IPv6 network interface found") | ||
} | ||
|
||
// Fetches configuration from both IPv4 and IPv6 metadata services | ||
func fetchConfigFromMetadataService(f *resource.Fetcher) ([]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this whole function is a bit confusing in the direction it wants to go. The intent is to get our config from the metadata service. If a metadata service has our config we should be happy regardless of which one had it.
Currently, we are fetching two times even if we already have the data we need to continue.
I think we can restructure this to be a little more clear.
First we need to decide based on the issue which metadata service should be primary?
Then it falls into something like
fetch from primary
if fail fetch from secondary
wdyt?
fmt.Println("Fetching zone id...") | ||
interfaces, err := net.Interfaces() | ||
if err != nil { | ||
return "", fmt.Errorf("error fetching zone id: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be more appropriate to say network interfaces as this is not really the zone id, and could confuse debugging. wdyt?
"error fetching network interfaces: %v", err
continue | ||
} | ||
|
||
for _, addr := range addrs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I get a small comment explaining how this is giving us the zoneID?
dbb2567
to
e6b084a
Compare
|
||
// the metadata server exists but doesn't contain any actual metadata, | ||
// assume that there is no config specified | ||
if err == resource.ErrNotFound { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we continue handling the not found error like it was done here?
I believe the same would need to happen for IPv6.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this and for the initial effort. This is less trivial than expected :D
Here's some feedback:
- The whole implementation (ipv4 / ipv6 detection) should be common to all providers. E.g: there is currently a similar ask for Scaleway provider (OpenStack single-stack IPv6 support #1897 (comment))
- For the implementation itself: it might increase the provisioning time as we need to wait for IPv4 to be seen as "unreachable" before giving up and trying IPv6
ignition/internal/resource/http.go
Line 309 in b111af1
duration := initialBackoff
Something I would propose:
- For a cloud provider, we define a map of URLs:
urls := map[IP]url.URL{
IPv4: url.URL{
Scheme: "http",
Host: "169.254.169.254",
Path: "openstack/latest/user_data",
},
IPv6: url.URL{
Scheme: "http",
Host: "[...]",
Path: "openstack/latest/user_data",
}
}
- In a common implementation, we start two go routines sharing a
success
channel. One routine trying the IPv4 stack and the other one using the IPv6 stack (with the correct IP for each) - of course if the initial URLs map contains only IPv4, we start only the IPv4 function (same goes for IPv6). - The first routine to return a
success
(i.e an Ignition configuration) will notify the channel and we can continue as usual (similar to what @prestist suggested here: https://github.com/coreos/ignition/pull/1909/files#r1985704411)
I'm not a big fan of using Go routines, but I think this situation might be a good usecase.
return nil, nil | ||
// Try fetching from IPv4 first | ||
response, err = f.FetchToBuffer(metadataServiceUrlIPv4, resource.FetchOptions{}) | ||
f.Logger.Debug("IPv6 URL:", metadataServiceUrlIPv6.Host) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f.Logger.Debug("IPv6 URL:", metadataServiceUrlIPv6.Host) | |
f.Logger.Debug("IPv4 URL:", metadataServiceUrlIPv6.Host) |
We're trying to fetch IPv4 first?
e6b084a
to
50a4776
Compare
Signed-off-by: Mathieu Tortuyaux <[email protected]>
This defines a wrapper that will try in paralell both IPv4 and IPv6 when the provider declares those two IPs. Signed-off-by: Mathieu Tortuyaux <[email protected]>
Hey @tormath1, thanks for reviewing and working on that! I went through your changes, and your approach makes complete sense. I agree with your comments. To align with your work, I’ve cherry-picked the changes you made in internal/resource/url.go, and I’ll adapt the provider side accordingly so we can implement something like what you did for Scaleway for OpenStack! Thanks again! |
@tormath1 I just finished implementing the helper! I'd really appreciate it if you could take a look and share your feedback. |
Thanks for the ping! I somehow missed it. I'll give a try to this implementation, I noticed some hiccups in the current approach but I think it should be easy to fix. I'll let you know :) |
In #1897 issue, we are requested to create support for IPv6 so Ignition can fetch the metadata in single-stack environments.
To achieve this, we added a new URL with the IPv6 endpoint. We also created logic to first attempt the IPv4 endpoint so If this fails, it will try the IPv6 one. If both endpoints fail, it will return the appropriate error.