Skip to content
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

foreman_host: Replace the "name" attribute with "fqdn" (computed) and "shortname" (required) #121

Merged
merged 10 commits into from
Jul 19, 2023
15 changes: 6 additions & 9 deletions docs/resources/foreman_host.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ A host managed by Foreman.
```
# Autogenerated example with required keys
resource "foreman_host" "example" {
name = "compute01.dc1.company.com"
}
```

Expand All @@ -31,27 +30,24 @@ The following arguments are supported:
- `hostgroup_id` - (Optional, Force New) ID of the hostgroup to assign to the host.
- `image_id` - (Optional, Force New) ID of an image to be used as base for this host when cloning
- `interfaces_attributes` - (Optional) Host interface information.
- `manage_build` - (Optional) REMOVED, please use the new 'managed' key instead. Create host only, don't set build status or manage power states
- `manage_power_operations` - (Optional) Manage power operations, e.g. power on, if host's build flag will be enabled.
- `managed` - (Optional) Whether or not this host is managed by Foreman. Create host only, don't set build status or manage power states.
- `medium_id` - (Optional, Force New) ID of the medium mounted on the host.
- `method` - (Optional) REMOVED - use build argument instead to manage build flag of host.
- `model_id` - (Optional) ID of the hardware model if applicable
- `name` - (Required, Force New) Host fully qualified domain name.
- `operatingsystem_id` - (Optional, Force New) ID of the operating system to put on the host.
- `owner_id` - (Optional) ID of the user or usergroup that owns the host.
- `owner_type` - (Optional) Owner of the host, must be either User ot Usergroup
- `parameters` - (Optional) A map of parameters that will be saved as host parameters in the machine config.
- `provision_method` - (Optional) A string, "build" to deploy from the network, "image" to deploy from a disk image.
- `provision_method` - (Optional, Force New) Sets the provision method in Foreman for this host: either network-based ('build') or image-based ('image')
- `puppet_class_ids` - (Optional) IDs of the applied puppet classes.
- `retry_count` - (Optional) Number of times to retry on a failed attempt to register or delete a host in foreman.
- `shortname` - (Required, Force New) The short name of this host. Example: when the FQDN is 'host01.example.org', then 'host01' is the short name.


## Attributes Reference

The following attributes are exported:

- `build` - Whether or not this host's build flag will be enabled in Foreman. Default is true, which means host will be built at next boot.
- `comment` - Add additional information about this host.Note: Changes to this attribute will trigger a host rebuild.
- `compute_attributes` - Hypervisor specific VM options. Must be a JSON string, as every compute provider has different attributes schema
- `compute_profile_id` -
Expand All @@ -61,21 +57,22 @@ The following attributes are exported:
- `domain_name` - The domain name of the host.
- `enable_bmc` - Enables PMI/BMC functionality. On create and update calls, having this enabled will force a host to poweroff, set next boot to PXE and power on. Defaults to `false`.
- `environment_id` - ID of the environment to assign to the host.
- `fqdn` - Host fully qualified domain name. Read-only value to be used in variables.
- `hostgroup_id` - ID of the hostgroup to assign to the host.
- `image_id` - ID of an image to be used as base for this host when cloning
- `interfaces_attributes` - Host interface information.
- `manage_build` - REMOVED, please use the new 'managed' key instead. Create host only, don't set build status or manage power states
- `manage_power_operations` - Manage power operations, e.g. power on, if host's build flag will be enabled.
- `managed` - Whether or not this host is managed by Foreman. Create host only, don't set build status or manage power states.
- `medium_id` - ID of the medium mounted on the host.
- `method` - REMOVED - use build argument instead to manage build flag of host.
- `model_id` - ID of the hardware model if applicable
- `name` - Host fully qualified domain name.
- `name` - Name of this host as stored in Foreman. Can be short name or FQDN, depending on your Foreman settings (especially the setting 'append_domain_name_for_hosts').
- `operatingsystem_id` - ID of the operating system to put on the host.
- `owner_id` - ID of the user or usergroup that owns the host.
- `owner_type` - Owner of the host, must be either User ot Usergroup
- `parameters` - A map of parameters that will be saved as host parameters in the machine config.
- `provision_method` - Sets the provision method in Foreman for this host: either network-based ('build') or image-based ('image')
- `puppet_class_ids` - IDs of the applied puppet classes.
- `retry_count` - Number of times to retry on a failed attempt to register or delete a host in foreman.
- `shortname` - The short name of this host. Example: when the FQDN is 'host01.example.org', then 'host01' is the short name.
- `token` - Build token. Can be used to signal to Foreman that a host build is complete.

61 changes: 55 additions & 6 deletions foreman/api/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/HanseMerkur/terraform-provider-utils/log"
)
Expand Down Expand Up @@ -48,6 +49,15 @@ type ForemanHost struct {
// Inherits the base object's attributes
ForemanObject

// Shortname, FQDN without DomainName.
// Not provided by the API, only available in templates with embedded Ruby
Shortname string

// The "fqdn" field in the Terraform schema exists only in the schema,
// not in this struct. Reason: name is a Foreman-managed field that can either hold
// the shortname or the FQDN, but the "fqdn" field is presented to the user
// and must be consistent.

// Whether or not to rebuild the host on reboot
Build bool `json:"build"`
// Current build status
Expand Down Expand Up @@ -98,7 +108,7 @@ type ForemanHost struct {
ComputeProfileId *int `json:"compute_profile_id,omitempty"`
// IDs of the puppet classes applied to the host
PuppetClassIds []int `json:"puppet_class_ids,omitempty"`
// Build token
// Build token, used by Foreman to provide a phone-home access token
Token string `json:"token,omitempty"`
// List of config groups to apply to the hostg
ConfigGroupIds []int `json:"config_group_ids"`
Expand Down Expand Up @@ -247,7 +257,7 @@ func (c *Client) SendPowerCommand(ctx context.Context, h *ForemanHost, cmd inter
// returned reference will have its ID and other API default values set by this
// function.
func (c *Client) CreateHost(ctx context.Context, h *ForemanHost, retryCount int) (*ForemanHost, error) {
log.Tracef("foreman/api/host.go#Create")
log.Tracef("foreman/api/host.go#CreateHost")

reqEndpoint := fmt.Sprintf("/%s", HostEndpointPrefix)

Expand Down Expand Up @@ -288,6 +298,10 @@ func (c *Client) CreateHost(ctx context.Context, h *ForemanHost, retryCount int)
return nil, sendErr
}

if err := constructShortname(&createdHost); err != nil {
return nil, err
}

createdHost.InterfacesAttributes = createdHost.InterfacesAttributesDecode
createdHost.PuppetClassIds = foremanObjectArrayToIdIntArray(createdHost.PuppetClassesDecode)
createdHost.ConfigGroupIds = foremanObjectArrayToIdIntArray(createdHost.ConfigGroupsDecode)
Expand All @@ -306,7 +320,7 @@ func (c *Client) CreateHost(ctx context.Context, h *ForemanHost, retryCount int)
// ReadHost reads the attributes of a ForemanHost identified by the supplied ID
// and returns a ForemanHost reference.
func (c *Client) ReadHost(ctx context.Context, id int) (*ForemanHost, error) {
log.Tracef("foreman/api/host.go#Read")
log.Tracef("foreman/api/host.go#ReadHost")

reqEndpoint := fmt.Sprintf("/%s/%d", HostEndpointPrefix, id)

Expand All @@ -326,6 +340,10 @@ func (c *Client) ReadHost(ctx context.Context, id int) (*ForemanHost, error) {
return nil, sendErr
}

if err := constructShortname(&readHost); err != nil {
return nil, err
}

computeAttributes, _ := c.readComputeAttributes(ctx, id)
if len(computeAttributes) > 0 {
readHost.ComputeAttributes = computeAttributes
Expand All @@ -342,7 +360,7 @@ func (c *Client) ReadHost(ctx context.Context, id int) (*ForemanHost, error) {
// supplied ForemanHost will be updated. A new ForemanHost reference is
// returned with the attributes from the result of the update operation.
func (c *Client) UpdateHost(ctx context.Context, h *ForemanHost, retryCount int) (*ForemanHost, error) {
log.Tracef("foreman/api/host.go#Update")
log.Tracef("foreman/api/host.go#UpdateHost")

reqEndpoint := fmt.Sprintf("/%s/%d", HostEndpointPrefix, h.Id)

Expand Down Expand Up @@ -382,6 +400,10 @@ func (c *Client) UpdateHost(ctx context.Context, h *ForemanHost, retryCount int)
return nil, sendErr
}

if err := constructShortname(&updatedHost); err != nil {
return nil, err
}

computeAttributes, _ := c.readComputeAttributes(ctx, h.Id)
if len(computeAttributes) > 0 {
updatedHost.ComputeAttributes = computeAttributes
Expand All @@ -397,7 +419,7 @@ func (c *Client) UpdateHost(ctx context.Context, h *ForemanHost, retryCount int)

// DeleteHost deletes the ForemanHost identified by the supplied ID
func (c *Client) DeleteHost(ctx context.Context, id int) error {
log.Tracef("foreman/api/host.go#Delete")
log.Tracef("foreman/api/host.go#DeleteHost")

reqEndpoint := fmt.Sprintf("/%s/%d", HostEndpointPrefix, id)

Expand All @@ -416,6 +438,7 @@ func (c *Client) DeleteHost(ctx context.Context, id int) error {

// Compute Attributes are only available via dedicated API endpoint. readComputeAttributes gets this endpoint.
func (c *Client) readComputeAttributes(ctx context.Context, id int) (map[string]interface{}, error) {
log.Tracef("foreman/api/host.go#readComputeAttributes")

reqEndpoint := fmt.Sprintf("/%s/%d/%s", HostEndpointPrefix, id, ComputeAttributesSuffix)

Expand All @@ -436,10 +459,36 @@ func (c *Client) readComputeAttributes(ctx context.Context, id int) (map[string]
}

readVmAttributesStr := make(map[string]interface{}, len(readVmAttributes))

for idx, val := range readVmAttributes {
readVmAttributesStr[idx] = val
}

return readVmAttributesStr, nil
}

func constructShortname(host *foremanHostDecode) error {
log.Tracef("foreman/api/host.go#constructShortname")

// Construct shortname from 'name'
if host.Shortname == "" {
before, after, found := strings.Cut(host.Name, ".")

// If no dot is found and shortname is not defined, Foreman probably does not expand hostnames with the domain name
if !found {
bitkeks marked this conversation as resolved.
Show resolved Hide resolved
host.Shortname = host.Name
return nil
}

// Sanity check
if host.DomainName != "" && host.DomainName != after {
log.Errorf("After Cut of host.Name to find the shortname, the domain part did not match the rest of the 'name' string")
}

// If all went well, set the shortname to the first string from FQDN ('name' in Foreman)
log.Debugf("constructShortname: Shortname will be set to first element from FQDN: %s", before)
host.Shortname = before
} else {
log.Debugf("constructShortname: host.Shortname is not empty (is %s), so nothing is done", host.Shortname)
}
return nil
}
Loading