-
Notifications
You must be signed in to change notification settings - Fork 13
metal_device: add hostname_prefix attribute #165
Conversation
This allows to make sure that every new device will have a unique device name, which often maps back to the OS hostname. This is especially important for Kubernetes where each node needs a new name.
Force the device to be re-created for the new value to propagate.
Thanks for opening this PR, @zimbatm! I see how this adds value beyond the random prefix in cases where the hostname should not be (or is troublesome if) reused, as in kubernetes nodes or logging data. There are a few changes I would like to consider:
|
If we drop the |
@zimbatm These hostnames are a little ridiculous :-) The EM API returns a short id (the first part of the device uuid), which is also used in the API generated hostnames. These short ids would have enough uniqueness, I think, but they wouldn't be known until after Device.Create is called. If the Create succeded, but the Update failed, we would rely on a custom diff function to determine if the correct hostname was in use (prefix + short id) and heal the hostname in the API. I like this approach because the suffix is meaningful, as it relates to resources used within the EM. This also provides uniqueness. Alternatively, since this prefix is just a long timestamp integer, we could translate it to a base64 string to carry the same detail in a more condensed (albeit less human readable) format. I made an example at https://play.golang.org/p/e6qwPnjT_G9:
|
The OS hostname would have the desired hostname so long as the Device.Update was called before the metadata service was inspected during the provisioning steps. The Update call would happen less than a second after the Create() call, so we can nearly guarantee that this will succeed, but it is still a race condition. Perhaps the base64 approach would be safer. |
well.. All of our suffixes would start with "ELf" for the next few months before moving on to "ELo". Base64 also includes "/" (and others, potentially padding characters too) that we would have to removed (effectively we need base62 [A-Za-z0-9]). |
hex.EncodeToString avoids the need for base62 with a longer string that also has a few months of repeated prefixes. In all but the short id approach, I find it confusing to have uniqueids that are mostly the same save a few bytes which are hard to discern on sight or sound. |
Would you agree to extend the EM API to take the Otherwise, I propose to add our own random ID generator that outputs base36. domain-related names generally aren't case sensitive so it's best to avoid the potential collision. |
The EM API supports a hostname prefix for batch device creation requests, including spot market. The suffix is an autoincrementing integer in those cases. I'm not sure about the precise format, but I suspect
This sounds good. |
^ what do you think of this? https://github.com/rs/xid |
id = PrefixedUniqueId(prefix) | ||
|
||
if _, ok := ids[id]; ok { | ||
t.Fatalf("Got duplicated id! %s", id) |
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.
It's possible but improbable for this to fail. I suppose we can keep the sanity check.
@@ -524,6 +546,8 @@ func resourceMetalDeviceCreate(d *schema.ResourceData, meta interface{}) error { | |||
return retErr | |||
} | |||
|
|||
// Pick the hostname validated by metal | |||
d.Set("hostname", newDevice.Hostname) |
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 don't think setting the hostname here is needed. SSH provisioners will not execute until after waitForActiveDevice and resourceMetalDeviceRead (which picks up the hostname) below.
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.
In the first part of this function, the desired hostname is computed and then passed to the Metal API to create the new device.
Then here we sync back the hostname attribute with what we got back from the Metal API.
That way the Metal API can decide to name the hostname otherwise, and the resource hostname attribute is computed based on that.
Type: schema.TypeString, | ||
Description: "The device name", | ||
Optional: true, | ||
ForceNew: true, |
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'm reluctant to accept the ForceNews. Why do you suppose this is needed?
@t0mk any thoughts?
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.
Overall, I believe it would be better for the provider if that field was immutable and replace the resource when it gets changed. In terms of backward compatibility, even if it changes the API, I don't believe that it will have much of an impact on users.
It removes a bit of logic on Update to rename the device on hostname change. In practice, I haven't seen devices being renamed to I suspect that this part of the code is buggy already.
If the device hostname changes, it won't reflect on the OS hostname automatically. I would rather see the device getting replaced and both map 1:1 all of the time.
Co-authored-by: Marques Johansson <github@displague.com>
Co-authored-by: Marques Johansson <github@displague.com>
Co-authored-by: Marques Johansson <github@displague.com>
Hey @zimbatm @displague, I think I understand the valid use case for a random prefix, but I think creating a resource field inside the provider for this is a bad idea:
Are you sure you can't achieve the random prefix outside of the provider internals with Terraform functions? I imagine there must be a way. I know you looked at the random provider (you mentioned https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet#example-usage
Overall I am against this PR. It looks like adding unnecessary complexity for sake of a single use case. But I will submit if there is business reasoning from Equinix. |
It's a limitation of Terraform. It's not possible to bind the lifecycle of two resources together without creating a cycle. resource "random_id" "hostname_suffix" {
keepers = {
// this creates a cycle
device_id = metal_device.my_host.id
}
}
resource "metal_device" "my_host" {
hostname = "my-prefix-${random_id.hostname_suffix.hex}"
# ...
} |
Would you accept if I made another PR where the |
@zimbatm I think yes, if it would (at least somehow) mimic the Javascript UI of the Web Console. In there, I still think it's not great, but at least it's similar to the Web Console and to Scaleway server: Also, we try to keep the dependencies low, so I'd be against adding @displague what do you think about this? |
waiting on @displague before doing any more work |
@zimbatm There was some interest from the API team in exposing the short id as a template variable in the hostname. There is already limited support for such things so it would be a matter of formalizing that feature and perhaps expanding on it. |
NOTE: This project is Deprecated. The Equinix provider has full support for existing Terraform managed Equinix Metal resources once Terraform configuration and state are adapted. The Equinix provider manages resources including Network Edge and Fabric in addition to Metal. Please review the Metal to Equinix provider migration guide. During the deprecation process we have migrated issues and PRs to the Equinix provider repository. However this PR contains content that I consider is more appropriate to continue in a github discussion instead of an issue/pr before resuming efforts. equinix/terraform-provider-equinix#213 Please, I encourage you to continue the conversation there. I think it's an interesting topic with good content that can help us define some rules that we want to follow for future features that may be different from the API behavior but useful for users. |
This allows making sure that every new device will have a unique hostname. There are a number of distributed systems like Kubernetes, Ceph, Redpanda, .. that assume that each host is unique by its hostname.
Using
random_string
to add a suffix to the hostname is not enough because it doesn't guarantee that therandom_string
will be re-created if themetal_device
is (eg: user_data change, resource was deleted manually, or tainted).This PR was inspired by the
aws_s3_bucket
resource.