Skip to content

Commit

Permalink
⭐️ support custom space ids for space resource (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-rock authored Feb 3, 2024
1 parent c6b6b35 commit 215d1fb
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 93 deletions.
3 changes: 2 additions & 1 deletion docs/resources/space.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ resource "mondoo_space" "my_space" {

### Optional

- `id` (String) Id of the space. Must be globally unique.
- `name` (String) Name of the space.

### Read-Only

- `id` (String) Id of the space. Must be globally within the organization.
- `mrn` (String) Mrn of the space.
4 changes: 2 additions & 2 deletions internal/provider/custom_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (r *customPolicyResource) Create(ctx context.Context, req resource.CreateRe
// Save data into Terraform state
data.Content = types.StringValue(string(policyBundleData))
data.Crc32Checksum = types.StringValue(checksum)
data.Mrns, _ = types.ListValueFrom(ctx, types.StringType, setCustomPolicy.SetCustomPolicyPayload.PolicyMrns)
data.Mrns, _ = types.ListValueFrom(ctx, types.StringType, setCustomPolicy.PolicyMrns)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)

}
Expand Down Expand Up @@ -267,7 +267,7 @@ func (r *customPolicyResource) Update(ctx context.Context, req resource.UpdateRe

data.Content = types.StringValue(string(policyBundleData))
data.Crc32Checksum = types.StringValue(checksum)
data.Mrns, _ = types.ListValueFrom(ctx, types.StringType, setCustomPolicy.SetCustomPolicyPayload.PolicyMrns)
data.Mrns, _ = types.ListValueFrom(ctx, types.StringType, setCustomPolicy.PolicyMrns)
}

// Save data into Terraform state
Expand Down
103 changes: 96 additions & 7 deletions internal/provider/gql.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,100 @@ func newDataUrl(content []byte) string {
return "data:application/x-yaml;base64," + base64.StdEncoding.EncodeToString(content)
}

type setCustomPolicyRequest struct {
SetCustomPolicyPayload struct {
PolicyMrns []mondoov1.String
} `graphql:"setCustomPolicy(input: $input)"`
type createSpacePayload struct {
Id mondoov1.ID
Mrn mondoov1.String
Name mondoov1.String
}

func (c *ExtendedGqlClient) CreateSpace(ctx context.Context, orgID string, id string, name string) (createSpacePayload, error) {
var createMutation struct {
CreateSpace createSpacePayload `graphql:"createSpace(input: $input)"`
}

var spaceID *mondoov1.String
if id != "" {
spaceID = mondoov1.NewStringPtr(mondoov1.String(id))
}

createInput := mondoov1.CreateSpaceInput{
Name: mondoov1.String(name),
ID: spaceID,
OrgMrn: mondoov1.String(orgPrefix + orgID),
}

tflog.Trace(ctx, "CreateSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", createInput),
})

err := c.Mutate(ctx, &createMutation, createInput, nil)
return createMutation.CreateSpace, err
}

func (c *ExtendedGqlClient) UpdateSpace(ctx context.Context, spaceID string, name string) error {
var updateMutation struct {
UpdateSpace struct {
Space struct {
Mrn mondoov1.String
Name mondoov1.String
}
} `graphql:"updateSpace(input: $input)"`
}
updateInput := mondoov1.UpdateSpaceInput{
Mrn: mondoov1.String(spacePrefix + spaceID),
Name: mondoov1.String(name),
}
tflog.Trace(ctx, "UpdateSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", updateInput),
})
return c.Mutate(ctx, &updateMutation, updateInput, nil)
}

func (c *ExtendedGqlClient) DeleteSpace(ctx context.Context, spaceID string) error {
var deleteMutation struct {
DeleteSpace mondoov1.String `graphql:"deleteSpace(spaceMrn: $spaceMrn)"`
}
variables := map[string]interface{}{
"spaceMrn": mondoov1.ID(spacePrefix + spaceID),
}

tflog.Trace(ctx, "DeleteSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", variables),
})

return c.Mutate(ctx, &deleteMutation, nil, variables)
}

type spacePayload struct {
Id string
Mrn string
Name string
Organization struct {
Id string
}
}

func (c *ExtendedGqlClient) SetCustomPolicy(ctx context.Context, scopeMrn string, overwriteVal *bool, policyBundleData []byte) (setCustomPolicyRequest, error) {
func (r *ExtendedGqlClient) GetSpace(ctx context.Context, mrn string) (spacePayload, error) {
var q struct {
Space spacePayload `graphql:"space(mrn: $mrn)"`
}
variables := map[string]interface{}{
"mrn": mondoov1.String(mrn),
}

err := r.Query(ctx, &q, variables)
if err != nil {
return spacePayload{}, err
}

return q.Space, nil
}

type setCustomPolicyPayload struct {
PolicyMrns []mondoov1.String
}

func (c *ExtendedGqlClient) SetCustomPolicy(ctx context.Context, scopeMrn string, overwriteVal *bool, policyBundleData []byte) (setCustomPolicyPayload, error) {
var overwrite *mondoov1.Boolean
if overwriteVal != nil {
overwrite = mondoov1.NewBooleanPtr(mondoov1.Boolean(*overwriteVal))
Expand All @@ -38,10 +125,12 @@ func (c *ExtendedGqlClient) SetCustomPolicy(ctx context.Context, scopeMrn string
Dataurl: mondoov1.String(newDataUrl(policyBundleData)),
}

var setCustomPolicy setCustomPolicyRequest
var setCustomPolicy struct {
SetCustomPolicyPayload setCustomPolicyPayload `graphql:"setCustomPolicy(input: $input)"`
}

err := c.Mutate(ctx, &setCustomPolicy, []mondoov1.SetCustomPolicyInput{setCustomPolicyInput}, nil)
return setCustomPolicy, err
return setCustomPolicy.SetCustomPolicyPayload, err
}

func (c *ExtendedGqlClient) AssignPolicy(ctx context.Context, spaceMrn string, action mondoov1.PolicyAction, policyMrns []string) error {
Expand Down
129 changes: 48 additions & 81 deletions internal/provider/space_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ func NewSpaceResource() resource.Resource {

// SpaceResource defines the resource implementation.
type SpaceResource struct {
client *mondoov1.Client
client *ExtendedGqlClient
}

// ProjectResourceModel describes the resource data model.
type ProjectResourceModel struct {
SpaceID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
OrgID types.String `tfsdk:"org_id"`
SpaceID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
OrgID types.String `tfsdk:"org_id"`
SpaceMrn types.String `tfsdk:"mrn"`
}

func (r *SpaceResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
Expand All @@ -57,7 +58,15 @@ func (r *SpaceResource) Schema(ctx context.Context, req resource.SchemaRequest,
Optional: true,
},
"id": schema.StringAttribute{
MarkdownDescription: "Id of the space. Must be globally within the organization.",
MarkdownDescription: "Id of the space. Must be globally unique.",
Computed: true,
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"mrn": schema.StringAttribute{
MarkdownDescription: "Mrn of the space.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
Expand Down Expand Up @@ -86,7 +95,7 @@ func (r *SpaceResource) Configure(ctx context.Context, req resource.ConfigureReq
return
}

r.client = client
r.client = &ExtendedGqlClient{client}
}

func (r *SpaceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
Expand All @@ -100,35 +109,22 @@ func (r *SpaceResource) Create(ctx context.Context, req resource.CreateRequest,
}

// Do GraphQL request to API to create the resource.
var createMutation struct {
CreateSpace struct {
Id mondoov1.ID
Mrn mondoov1.String
Name mondoov1.String
} `graphql:"createSpace(input: $input)"`
}
createInput := mondoov1.CreateSpaceInput{
Name: mondoov1.String(data.Name.ValueString()),
OrgMrn: mondoov1.String(orgPrefix + data.OrgID.ValueString()),
}

tflog.Trace(ctx, "CreateSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", createInput),
})

err := r.client.Mutate(ctx, &createMutation, createInput, nil)
payload, err := r.client.CreateSpace(ctx, data.OrgID.ValueString(), data.SpaceID.ValueString(), data.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create space, got error: %s", err))
return
}

// Save space mrn into the Terraform state.
data.Name = types.StringValue(string(createMutation.CreateSpace.Name))
id, ok := createMutation.CreateSpace.Id.(string)
data.Name = types.StringValue(string(payload.Name))

id, ok := payload.Id.(string)
if ok {
data.SpaceID = types.StringValue(id)
}

data.SpaceMrn = types.StringValue(string(payload.Mrn))

// Write logs using the tflog package
tflog.Trace(ctx, "created a space resource")

Expand Down Expand Up @@ -160,23 +156,23 @@ func (r *SpaceResource) Update(ctx context.Context, req resource.UpdateRequest,
return
}

// Do GraphQL request to API to update the resource.
var updateMutation struct {
UpdateSpace struct {
Space struct {
Mrn mondoov1.String
Name mondoov1.String
}
} `graphql:"updateSpace(input: $input)"`
}
updateInput := mondoov1.UpdateSpaceInput{
Mrn: mondoov1.String(spacePrefix + data.SpaceID.ValueString()),
Name: mondoov1.String(data.Name.ValueString()),
// ensure space id is not changed
var stateSpaceID string
req.State.GetAttribute(ctx, path.Root("id"), &stateSpaceID)

var planSpaceID string
req.Plan.GetAttribute(ctx, path.Root("id"), &planSpaceID)

if stateSpaceID != planSpaceID {
resp.Diagnostics.AddError(
"Space ID cannot be changed",
"Space ID cannot be changed",
)
return
}
tflog.Trace(ctx, "UpdateSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", updateInput),
})
err := r.client.Mutate(ctx, &updateMutation, updateInput, nil)

// Do GraphQL request to API to update the resource.
err := r.client.UpdateSpace(ctx, data.SpaceID.ValueString(), data.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update space, got error: %s", err))
return
Expand All @@ -197,59 +193,30 @@ func (r *SpaceResource) Delete(ctx context.Context, req resource.DeleteRequest,
}

// Do GraphQL request to API to delete the resource.
var deleteMutation struct {
DeleteSpace mondoov1.String `graphql:"deleteSpace(spaceMrn: $spaceMrn)"`
}
variables := map[string]interface{}{
"spaceMrn": mondoov1.ID(spacePrefix + data.SpaceID.ValueString()),
}

tflog.Trace(ctx, "DeleteSpaceInput", map[string]interface{}{
"input": fmt.Sprintf("%+v", variables),
})

err := r.client.Mutate(ctx, &deleteMutation, nil, variables)
err := r.client.DeleteSpace(ctx, data.SpaceID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete space, got error: %s", err))
return
}
}

func (r *SpaceResource) readSpace(ctx context.Context, mrn string) (ProjectResourceModel, error) {
var q struct {
Space struct {
Id string
Mrn string
Name string
Organization struct {
Id string
}
} `graphql:"space(mrn: $mrn)"`
}
variables := map[string]interface{}{
"mrn": mondoov1.String(mrn),
}

err := r.client.Query(ctx, &q, variables)
if err != nil {
return ProjectResourceModel{}, err
}

return ProjectResourceModel{
SpaceID: types.StringValue(q.Space.Id),
Name: types.StringValue(q.Space.Name),
OrgID: types.StringValue(q.Space.Organization.Id),
}, nil
}

func (r *SpaceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
mrn := spacePrefix + req.ID
model, err := r.readSpace(ctx, mrn)
spacePayload, err := r.client.GetSpace(ctx, mrn)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to retrieve space, got error: %s", err))
return
}

model := ProjectResourceModel{
SpaceID: types.StringValue(spacePayload.Id),
SpaceMrn: types.StringValue(spacePayload.Mrn),
Name: types.StringValue(spacePayload.Name),
OrgID: types.StringValue(spacePayload.Organization.Id),
}

resp.State.SetAttribute(ctx, path.Root("id"), model.SpaceID)
resp.State.SetAttribute(ctx, path.Root("name"), model.Name)
resp.State.SetAttribute(ctx, path.Root("org_id"), model.OrgID)
resp.State.SetAttribute(ctx, path.Root("mrn"), model.SpaceMrn)
}
Loading

0 comments on commit 215d1fb

Please sign in to comment.