-
Notifications
You must be signed in to change notification settings - Fork 16
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
providers/azure: support Azure IMDS #39
Conversation
e0a88fb
to
d0ea0b2
Compare
1ea879a
to
63b2cfc
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.
👋 Thanks for working on this! A few comments from upstream.
internal/resource/url.go
Outdated
@@ -542,6 +543,8 @@ func (f *Fetcher) uncompress(r io.Reader, opts FetchOptions) (io.ReadCloser, err | |||
return ioutil.NopCloser(r), nil | |||
case "gzip": | |||
return gzip.NewReader(r) | |||
case "base64": |
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'd prefer that we not do this here, and handle base64-decoding in the Azure provider instead. Otherwise this function will be a mix of cases that are supported by the Resource.Compression
spec field and cases that are supported only for internal convenience.
@@ -34,5 +34,5 @@ const ( | |||
|
|||
// FetchConfig implements the platform.NewFetcher interface. | |||
func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { | |||
return azure.FetchFromOvfDevice(f, []string{CDS_FSTYPE_UDF, CDS_FSTYPE_ISO9660}) | |||
return azure.FetchFromAzureMetadata(f, []string{CDS_FSTYPE_UDF, CDS_FSTYPE_ISO9660}) |
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 confirmed that Azure Stack always supports the metadata service? If not, we should probably keep FetchFromOvfDevice
here, and make FetchFromAzureMetadata
a wrapper around it.
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.
Right - thanks for the heads-up ! According to the documentation1:
The Azure Instance Metadata Service isn't supported on Azure Stack Hub.
Footnotes
internal/providers/azure/azure.go
Outdated
@@ -56,15 +57,38 @@ const ( | |||
CDS_FSTYPE_UDF = "udf" | |||
) | |||
|
|||
// FetchConfig wraps FetchOvfDevice to implement the platform.NewFetcher interface. | |||
var ( | |||
imdsURL = url.URL{ |
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.
Nit: maybe imdsUserdataURL
?
internal/providers/azure/azure.go
Outdated
// we first try to config from IMDS, in case of failure we fallback on the custom data. | ||
data, err := f.FetchToBuffer(imdsURL, resource.FetchOptions{Compression: "base64"}) | ||
if err == nil { | ||
return util.ParseConfig(logger, data) |
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.
For user debugging purposes, let's log at Info
level if we've read the config from userdata and if we've read it from custom data.
internal/providers/azure/azure.go
Outdated
|
||
// we first try to config from IMDS, in case of failure we fallback on the custom data. | ||
data, err := f.FetchToBuffer(imdsURL, resource.FetchOptions{Compression: "base64"}) | ||
if err == nil { |
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.
If there's any error, we're falling through and failing to report it. I assume the metadata service is always available, and returns 404 if userdata is missing? If so, we should probably fall through on resource.ErrNotFound
, and error out otherwise.
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.
Just checked and if userdata is missing it seems to return 200 with an empty string:
curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance/compute/userData?api-version=2021-01-01&format=text" -v
* Trying 169.254.169.254:80...
* Connected to 169.254.169.254 (169.254.169.254) port 80 (#0)
> GET /metadata/instance/compute/userData?api-version=2021-01-01&format=text HTTP/1.1
> Host: 169.254.169.254
> User-Agent: curl/7.79.1
> Accept: */*
> Metadata:true
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Server: IMDS/150.870.65.597
< Date: Thu, 14 Apr 2022 09:07:49 GMT
< Content-Length: 0
<
* Connection #0 to host 169.254.169.254 left intact
EDIT: added a check for data length
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.
Ah, okay. We're still falling through, though. Presumably we should be doing something like:
} else if err != errors.ErrEmpty {
return types.Config{}, report.Report{}, err
}
Otherwise, an error accessing the IMDS server could cause Ignition to conclude that no userdata was provided and allow the node to boot unconfigured.
6f0f27a
to
d419951
Compare
internal/providers/azure/azure.go
Outdated
|
||
// fetchFromAzureMetadata first tries to fetch userData from IMDS then fallback on customData in case | ||
// of failure or empty config. | ||
func fetchFromAzureMetadata(f *resource.Fetcher) (types.Config, report.Report, 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.
Nit: let's move this above the previous function so the file reads consistently from top to bottom.
internal/providers/azure/azure.go
Outdated
n := len(data) | ||
|
||
if n == 0 { | ||
return nil, fmt.Errorf("userdata is empty") |
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.
return nil, fmt.Errorf("userdata is empty") | |
return nil, errors.ErrEmpty |
return nil, fmt.Errorf("fetching to buffer: %w", err) | ||
} | ||
|
||
n := len(data) |
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.
nit: unnecessary variable
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.
declared because because used twice:
- to check if length is 0
- later to decode base64
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.
Argh, yup, I had missed that.
internal/providers/azure/azure.go
Outdated
|
||
// we first try to config from IMDS, in case of failure we fallback on the custom data. | ||
data, err := f.FetchToBuffer(imdsURL, resource.FetchOptions{Compression: "base64"}) | ||
if err == nil { |
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.
Ah, okay. We're still falling through, though. Presumably we should be doing something like:
} else if err != errors.ErrEmpty {
return types.Config{}, report.Report{}, err
}
Otherwise, an error accessing the IMDS server could cause Ignition to conclude that no userdata was provided and allow the node to boot unconfigured.
d419951
to
87639d8
Compare
@bgilbert thanks for the review - comments have been addressed and a CI test is running. |
Azure userdata fetching is now divided into two parts: * try to fetch userdata from IMDS * in case of failure: try to fetch custom data from CDROM Signed-off-by: Mathieu Tortuyaux <mtortuyaux@microsoft.com>
Update `golangci-lint` to version v1.45.0 which has support for go 1.18 Signed-off-by: Mathieu Tortuyaux <mtortuyaux@microsoft.com>
`strings.Title()` is deprecated in go 1.18, so change the affected tests to use different constants Signed-off-by: Mathieu Tortuyaux <mtortuyaux@microsoft.com>
1d3355c
to
3fa7f8b
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.
LGTM!
With the upstream PR merged I would say that we can either cherry pick directly or better update to a newer version since we have to do this anyway sooner or later |
it should come with the next release of ignition in Flatcar. |
In this PR, we add support for Azure IMDS on top of existing CustomData. If it fails to fetch base64 encoded userData Ignition will fallback on CustomData.
provider/azure: try to fetch user-data from IMDS
could be upstreamed once the tests / validation are OK.Note for reviewers
config: update headers_test to stop using strings.Title()
andci: add testing with Go 1.18
are from the upstream - it makes the CI happy.