From bd7ac597518926039ccaa40fb407b11c508b8ae5 Mon Sep 17 00:00:00 2001 From: Jake Hyde Date: Tue, 18 Apr 2023 15:58:41 -0400 Subject: [PATCH] Add hostname override flag to create --- commands/create.go | 28 ++++++++++++++++++--------- libmachine/host/host.go | 3 ++- libmachine/libmachine.go | 2 +- libmachine/provision/custom_script.go | 12 ++++++++---- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/commands/create.go b/commands/create.go index a3da9edf56..cc161ade72 100644 --- a/commands/create.go +++ b/commands/create.go @@ -138,6 +138,11 @@ var ( Usage: "Use a custom provisioning script instead of installing docker", Value: "", }, + cli.StringFlag{ + Name: "hostname-override", + Usage: "Specify hostname to use during cloud-init instead of default generated hostname", + Value: "", + }, } ) @@ -231,6 +236,7 @@ func cmdCreateInner(c CommandLine, api libmachine.API) error { osFlag := drivers.DriverOSFlag(h.Driver) customInstallScript := c.String("custom-install-script") + h.HostOptions.HostnameOverride = c.String("hostname-override") if customInstallScript != "" { h.HostOptions.CustomInstallScript = customInstallScript h.HostOptions.AuthOptions = nil @@ -238,7 +244,7 @@ func cmdCreateInner(c CommandLine, api libmachine.API) error { h.HostOptions.SwarmOptions = nil if userdataFlag != "" { - err = updateUserdataFile(driverOpts, name, userdataFlag, osFlag, customInstallScript) + err = updateUserdataFile(driverOpts, name, h.HostOptions.HostnameOverride, userdataFlag, osFlag, customInstallScript) if err != nil { return fmt.Errorf("[cmdCreateInner] could not alter cloud-init file: %v", err) } @@ -498,7 +504,7 @@ func gzipEncode(data []byte) (string, error) { // updateUserdataFile If the user has provided a userdata file, then we add the customInstallScript to their userdata file. // This assumes that the user-provided userdata file start with a shebang or `#cloud-config` // If the user has not provided any userdata file, then we set the customInstallScript as the userdata file. -func updateUserdataFile(driverOpts *rpcdriver.RPCFlags, machineName, userdataFlag, osFlag, customInstallScript string) error { +func updateUserdataFile(driverOpts *rpcdriver.RPCFlags, machineName, hostname, userdataFlag, osFlag, customInstallScript string) error { var userdataContent []byte var err error userdataFile := driverOpts.String(userdataFlag) @@ -508,13 +514,13 @@ func updateUserdataFile(driverOpts *rpcdriver.RPCFlags, machineName, userdataFla // Always convert to cloud config if user data is not provided userdataContent = []byte("#cloud-config") } else { - userdataContent, err = ioutil.ReadFile(userdataFile) + userdataContent, err = os.ReadFile(userdataFile) if err != nil { return err } } - customScriptContent, err := ioutil.ReadFile(customInstallScript) + customScriptContent, err := os.ReadFile(customInstallScript) if err != nil { return err } @@ -527,7 +533,7 @@ func updateUserdataFile(driverOpts *rpcdriver.RPCFlags, machineName, userdataFla } defer modifiedUserdataFile.Close() - if err := replaceUserdataFile(machineName, machineOS, userdataContent, customScriptContent, modifiedUserdataFile); err != nil { + if err := replaceUserdataFile(machineName, machineOS, hostname, userdataContent, customScriptContent, modifiedUserdataFile); err != nil { return err } @@ -540,7 +546,7 @@ func updateUserdataFile(driverOpts *rpcdriver.RPCFlags, machineName, userdataFla // and passes the script path to commonCloudConfig // RK - sets the hostname based on OS as cloud-init (linux) and cloudbase-init (windows) diverge // on how hostnames are set in cloud-config (userdata) -func writeCloudConfig(machineName, encodedData, machineOS string, cf map[interface{}]interface{}, newUserDataFile *os.File) error { +func writeCloudConfig(machineName, encodedData, machineOS, hostname string, cf map[interface{}]interface{}, newUserDataFile *os.File) error { command := "sh" path := "/usr/local/custom_script/install.sh" key := "hostname" @@ -555,7 +561,11 @@ func writeCloudConfig(machineName, encodedData, machineOS string, cf map[interfa key = "set_hostname" } if _, ok := cf[key]; !ok { - cf[key] = machineName + if hostname != "" { + cf[key] = hostname + } else { + cf[key] = machineName + } log.Debugf("[writeCloudConfig] wrote %s field for %s machine %s", key, machineOS, machineName) } return commonCloudConfig(encodedData, command, path, cf, newUserDataFile) @@ -598,7 +608,7 @@ func commonCloudConfig(encodedData, command, path string, cf map[interface{}]int // temp file for this content. // If the user-provided userdata file starts with a shebang, then we can add it to the customScriptContent and add data to the `runcmd` directive. // fi the user-provided userdata file is in cloud-config format, then we add the customScriptContent to the `runcmd` directive. -func replaceUserdataFile(machineName, machineOS string, userdataContent, customScriptContent []byte, newUserDataFile *os.File) error { +func replaceUserdataFile(machineName, machineOS, hostname string, userdataContent, customScriptContent []byte, newUserDataFile *os.File) error { var err error var encodedData string cf := make(map[interface{}]interface{}) @@ -632,7 +642,7 @@ func replaceUserdataFile(machineName, machineOS string, userdataContent, customS return fmt.Errorf("existing userdata file does not begin with '#!' or '#cloud-config'") } - return writeCloudConfig(machineName, encodedData, machineOS, cf, newUserDataFile) + return writeCloudConfig(machineName, encodedData, machineOS, hostname, cf, newUserDataFile) } // appendValueToListInCloudConfig appends a value to a list or creates one if it does not exist at the given key within the cloud config diff --git a/libmachine/host/host.go b/libmachine/host/host.go index 43e62a4240..a326af52d3 100644 --- a/libmachine/host/host.go +++ b/libmachine/host/host.go @@ -53,6 +53,7 @@ type Options struct { Memory int Disk int CustomInstallScript string + HostnameOverride string MachineOS string EngineOptions *engine.Options SwarmOptions *swarm.Options @@ -303,7 +304,7 @@ func (h *Host) Provision() error { if h.HostOptions.CustomInstallScript != "" { log.Infof("Machine %s was provisioned with a custom install script, using this script for provisioning", h.Name) - return provision.WithCustomScript(provisioner, h.HostOptions.CustomInstallScript) + return provision.WithCustomScript(provisioner, h.HostOptions.CustomInstallScript, h.HostOptions.HostnameOverride) } return provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions) diff --git a/libmachine/libmachine.go b/libmachine/libmachine.go index 72b47c96e8..b5f546c60b 100644 --- a/libmachine/libmachine.go +++ b/libmachine/libmachine.go @@ -177,7 +177,7 @@ func (api *Client) performCreate(h *host.Host) error { log.Infof("Provisioning with %s...", provisioner.String()) if h.HostOptions.CustomInstallScript != "" { log.Infof("Provisioning with custom install script via SSH, not installing Docker...") - return provision.WithCustomScript(provisioner, h.HostOptions.CustomInstallScript) + return provision.WithCustomScript(provisioner, h.HostOptions.CustomInstallScript, h.HostOptions.HostnameOverride) } else { if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil { return err diff --git a/libmachine/provision/custom_script.go b/libmachine/provision/custom_script.go index 143421ab71..57bfa8bc3a 100644 --- a/libmachine/provision/custom_script.go +++ b/libmachine/provision/custom_script.go @@ -2,17 +2,21 @@ package provision import ( "fmt" - "io/ioutil" + "os" "github.com/rancher/machine/libmachine/provision/pkgaction" ) -func WithCustomScript(provisioner Provisioner, customScriptPath string) error { +func WithCustomScript(provisioner Provisioner, customScriptPath, hostname string) error { if provisioner == nil { return nil } - if err := provisioner.SetHostname(provisioner.GetDriver().GetMachineName()); err != nil { + if hostname == "" { + hostname = provisioner.GetDriver().GetMachineName() + } + + if err := provisioner.SetHostname(hostname); err != nil { return err } @@ -22,7 +26,7 @@ func WithCustomScript(provisioner Provisioner, customScriptPath string) error { } } - customScriptContents, err := ioutil.ReadFile(customScriptPath) + customScriptContents, err := os.ReadFile(customScriptPath) if err != nil { return fmt.Errorf("unable to read file %s: %v", customScriptPath, err) }