-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Katello lifecycle environments resource and data source (#149)
Adds the "lifecycle environment" resources from Katello to Terraform, as both resource and data source. Tested on Katello 4.9: data source READ, resource CREATE/READ/UPDATE/DELETE. The handling of API prefixes for Katello in client.go might conflict with other PRs, this code block should be refactored. An example shows how the first lifecycle environment in a path can be created from the Library root environment. Resolves #141 Signed-off-by: Dominik Pataky <pataky@mindful-security.eu>
- Loading branch information
Showing
9 changed files
with
609 additions
and
58 deletions.
There are no files selected for viewing
36 changes: 36 additions & 0 deletions
36
docs/data-sources/foreman_katello_lifecycle_environment.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
# foreman_katello_lifecycle_environment | ||
|
||
|
||
Lifecycle environments group hosts into logical stages, example dev/test/prod. | ||
|
||
|
||
## Example Usage | ||
|
||
``` | ||
# Autogenerated example with required keys | ||
data "foreman_katello_lifecycle_environment" "example" { | ||
name = "Library" | ||
} | ||
``` | ||
|
||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
- `name` - (Required) Name of the lifecycle environment. | ||
|
||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
- `description` - Description for the lifecycle environment | ||
- `label` - Label for the lifecycle environment. Cannot be changed after creation. By default set to the name, with underscores as spaces replacement. | ||
- `library` - Specifies if this environment is the special 'Library' root environment. | ||
- `name` - Name of the lifecycle environment. | ||
- `organization_id` - | ||
- `prior_id` - ID of the prior lifecycle environment. Use '1' to refer to the built-in 'Library' root environment. | ||
- `successor_id` - | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
|
||
# foreman_katello_lifecycle_environment | ||
|
||
|
||
Lifecycle environments group hosts into logical stages, example dev/test/prod. | ||
|
||
|
||
## Example Usage | ||
|
||
``` | ||
# Autogenerated example with required keys | ||
resource "foreman_katello_lifecycle_environment" "example" { | ||
name = "My new env" | ||
organization_id = 1 | ||
prior_id = data.foreman_katello_lifecycle_environment.library.id | ||
} | ||
``` | ||
|
||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
- `description` - (Optional) Description for the lifecycle environment | ||
- `label` - (Optional, Force New) Label for the lifecycle environment. Cannot be changed after creation. By default set to the name, with underscores as spaces replacement. | ||
- `name` - (Required) Name of the lifecycle environment. | ||
- `organization_id` - (Required) | ||
- `prior_id` - (Required) ID of the prior lifecycle environment. Use '1' to refer to the built-in 'Library' root environment. | ||
|
||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
- `description` - Description for the lifecycle environment | ||
- `label` - Label for the lifecycle environment. Cannot be changed after creation. By default set to the name, with underscores as spaces replacement. | ||
- `library` - Specifies if this environment is the special 'Library' root environment. | ||
- `name` - Name of the lifecycle environment. | ||
- `organization_id` - | ||
- `prior_id` - ID of the prior lifecycle environment. Use '1' to refer to the built-in 'Library' root environment. | ||
- `successor_id` - | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Get the root 'Library' environment as data source | ||
data "foreman_katello_lifecycle_environment" "library" { | ||
name = "Library" | ||
} | ||
|
||
// Then create a new lifecycle environment which uses the Library as the prior environment | ||
resource "foreman_katello_lifecycle_environment" "newenv" { | ||
name = "My new lifecycle env" | ||
prior_id = data.foreman_katello_lifecycle_environment.library.id | ||
organization_id = 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package api | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/terraform-coop/terraform-provider-foreman/foreman/utils" | ||
"net/http" | ||
) | ||
|
||
const ( | ||
LifecycleEnvironmentEndpointPrefix = "/katello/api/environments" | ||
LifecycleEnvironmentById = LifecycleEnvironmentEndpointPrefix + "/%d" // :id | ||
LifecycleEnvironmentPathsByOrg = "/katello/api/organizations/%d/environments/paths" // :organization_id | ||
) | ||
|
||
type ContentViews struct { | ||
Name string `json:"name"` | ||
Id int `json:"id"` | ||
} | ||
|
||
type Organization struct { | ||
Name string `json:"name"` | ||
Label string `json:"label"` | ||
Id int `json:"id"` | ||
} | ||
|
||
type LifecycleEnvironment struct { | ||
ForemanObject | ||
|
||
Label string `json:"label"` | ||
Description string `json:"description"` | ||
OrganizationId int `json:"organization_id"` | ||
Organization Organization `json:"organization"` | ||
|
||
// Is this LifecycleEnvironment a library? | ||
Library bool `json:"library"` | ||
|
||
// Container Image Registry related | ||
RegistryNamePattern string `json:"registry_name_pattern"` | ||
RegistryUnauthenticatedPull bool `json:"registry_unauthenticated_pull"` | ||
|
||
Prior struct { | ||
Name string `json:"name"` | ||
Id int `json:"id"` | ||
} `json:"prior"` | ||
|
||
Successor struct { | ||
Name string `json:"name"` | ||
Id int `json:"id"` | ||
} `json:"successor"` | ||
|
||
Counts struct { | ||
ContentHosts int `json:"content_hosts"` | ||
ContentViews int `json:"content_views"` | ||
} `json:"counts"` | ||
|
||
ContentViews []ContentViews `json:"content_views"` | ||
} | ||
|
||
func (lce *LifecycleEnvironment) MarshalJSON() ([]byte, error) { | ||
jsonMap := map[string]interface{}{ | ||
"id": lce.Id, | ||
"name": lce.Name, | ||
"description": lce.Description, | ||
"organization_id": lce.OrganizationId, | ||
"label": lce.Label, | ||
"prior_id": lce.Prior.Id, | ||
} | ||
return json.Marshal(jsonMap) | ||
} | ||
|
||
func (c *Client) QueryLifecycleEnvironment(ctx context.Context, d *LifecycleEnvironment) (QueryResponse, error) { | ||
utils.TraceFunctionCall() | ||
|
||
queryResponse := QueryResponse{} | ||
|
||
endpoint := LifecycleEnvironmentEndpointPrefix | ||
req, err := c.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) | ||
if err != nil { | ||
return queryResponse, err | ||
} | ||
|
||
// dynamically build the query based on the attributes | ||
reqQuery := req.URL.Query() | ||
name := `"` + d.Name + `"` | ||
reqQuery.Set("search", "name="+name) | ||
|
||
req.URL.RawQuery = reqQuery.Encode() | ||
err = c.SendAndParse(req, &queryResponse) | ||
if err != nil { | ||
return queryResponse, err | ||
} | ||
|
||
utils.Debugf("queryResponse: %+v", queryResponse) | ||
|
||
var results []LifecycleEnvironment | ||
resultsBytes, err := json.Marshal(queryResponse.Results) | ||
if err != nil { | ||
return queryResponse, err | ||
} | ||
err = json.Unmarshal(resultsBytes, &results) | ||
if err != nil { | ||
return queryResponse, err | ||
} | ||
|
||
// convert the search results from []ForemanImage to []interface | ||
// and set the search results on the query | ||
iArr := make([]interface{}, len(results)) | ||
for idx, val := range results { | ||
iArr[idx] = val | ||
} | ||
queryResponse.Results = iArr | ||
|
||
return queryResponse, nil | ||
} | ||
|
||
func (c *Client) CreateKatelloLifecycleEnvironment(ctx context.Context, lce *LifecycleEnvironment) (*LifecycleEnvironment, error) { | ||
utils.TraceFunctionCall() | ||
|
||
endpoint := LifecycleEnvironmentEndpointPrefix | ||
|
||
jsonBytes, err := c.WrapJSONWithTaxonomy(nil, lce) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req, err := c.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(jsonBytes)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var createdLce LifecycleEnvironment | ||
err = c.SendAndParse(req, &createdLce) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
utils.Debugf("createdLce: %+v", createdLce) | ||
|
||
return &createdLce, nil | ||
} | ||
|
||
func (c *Client) ReadKatelloLifecycleEnvironment(ctx context.Context, d *LifecycleEnvironment) (*LifecycleEnvironment, error) { | ||
utils.TraceFunctionCall() | ||
|
||
reqEndpoint := fmt.Sprintf(LifecycleEnvironmentById, d.Id) | ||
var lce LifecycleEnvironment | ||
|
||
req, err := c.NewRequestWithContext(ctx, http.MethodGet, reqEndpoint, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = c.SendAndParse(req, &lce) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
utils.Debugf("read LifecycleEnv: %+v", lce) | ||
|
||
return &lce, nil | ||
} | ||
|
||
func (c *Client) UpdateKatelloLifecycleEnvironment(ctx context.Context, lce *LifecycleEnvironment) (*LifecycleEnvironment, error) { | ||
utils.TraceFunctionCall() | ||
|
||
endpoint := fmt.Sprintf(LifecycleEnvironmentById, lce.Id) | ||
|
||
jsonBytes, err := c.WrapJSONWithTaxonomy(nil, lce) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
utils.Debugf("jsonBytes: %s", jsonBytes) | ||
|
||
req, err := c.NewRequestWithContext(ctx, http.MethodPut, endpoint, bytes.NewBuffer(jsonBytes)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var updatedLce LifecycleEnvironment | ||
err = c.SendAndParse(req, &updatedLce) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
utils.Debugf("updatedLce: %+v", updatedLce) | ||
|
||
return &updatedLce, nil | ||
} | ||
|
||
func (c *Client) DeleteKatelloLifecycleEnvironment(ctx context.Context, id int) error { | ||
utils.TraceFunctionCall() | ||
|
||
endpoint := fmt.Sprintf(LifecycleEnvironmentById, id) | ||
|
||
req, err := c.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.SendAndParse(req, nil) | ||
} |
65 changes: 65 additions & 0 deletions
65
foreman/data_source_foreman_katello_lifecycle_environment.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package foreman | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/HanseMerkur/terraform-provider-utils/autodoc" | ||
"github.com/HanseMerkur/terraform-provider-utils/helper" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/terraform-coop/terraform-provider-foreman/foreman/api" | ||
"github.com/terraform-coop/terraform-provider-foreman/foreman/utils" | ||
) | ||
|
||
func dataSourceForemanKatelloLifecycleEnvironment() *schema.Resource { | ||
r := resourceForemanKatelloLifecycleEnvironment() | ||
ds := helper.DataSourceSchemaFromResourceSchema(r.Schema) | ||
|
||
// define searchable attributes for the data source | ||
ds["name"] = &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: fmt.Sprintf("Name of the lifecycle environment. %s \"Library\"", autodoc.MetaExample), | ||
} | ||
|
||
return &schema.Resource{ | ||
ReadContext: dataSourceForemanKatelloLifecycleRead, | ||
Schema: ds, | ||
} | ||
} | ||
|
||
func dataSourceForemanKatelloLifecycleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
utils.TraceFunctionCall() | ||
|
||
client := meta.(*api.Client) | ||
lce := buildForemanKatelloLifecycleEnvironment(d) | ||
|
||
utils.Debugf("lifecycle env: %+v", lce) | ||
|
||
queryResponse, err := client.QueryLifecycleEnvironment(ctx, lce) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
if queryResponse.Subtotal == 0 { | ||
return diag.Errorf("data source lifecycle_environment returned no results") | ||
} else if queryResponse.Subtotal > 1 { | ||
return diag.Errorf("data source lifecycle_environment returned more than 1 result") | ||
} | ||
|
||
if queryLce, ok := queryResponse.Results[0].(api.LifecycleEnvironment); !ok { | ||
return diag.Errorf( | ||
"data source results contain unexpected type. Expected "+ | ||
"[api.LifecycleEnvironment], got [%T]", | ||
queryResponse.Results[0], | ||
) | ||
} else { | ||
lce = &queryLce | ||
} | ||
|
||
utils.Debugf("lifecycle env: %+v", lce) | ||
|
||
setResourceDataFromForemanKatelloLifecycleEnvironment(d, lce) | ||
|
||
return nil | ||
} |
Oops, something went wrong.