diff --git a/client/monte_carlo_client.go b/client/monte_carlo_client.go index 669f928..f616e8a 100644 --- a/client/monte_carlo_client.go +++ b/client/monte_carlo_client.go @@ -283,3 +283,23 @@ type UpdateUserAuthorizationGroupMembership struct { } } `graphql:"updateUserAuthorizationGroupMembership(memberUserId: $memberUserId, groupNames: $groupNames)"` } + +type CreateOrUpdateMonteCarloConfigTemplate struct { + CreateOrUpdateMonteCarloConfigTemplate struct { + Response struct { + ChangesApplied bool + ErrorsAsJson string + InfoAsJson string + ResourceModifications []struct { + Type string + Description string + ResourceAsJson string + IsSignificantChange bool + DiffString string + ResourceType string + ResourceIndex int + } + WarningsAsJson string + } + } `graphql:"createOrUpdateMonteCarloConfigTemplate(configTemplateJson: $configTemplateJson, dryRun: $dryRun, namespace: $namespace, resource: $resource)"` +} diff --git a/go.mod b/go.mod index dced18b..130b03d 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module github.com/kiwicom/terraform-provider-montecarlo go 1.21.1 require ( + github.com/google/uuid v1.3.1 github.com/hashicorp/terraform-plugin-framework v1.4.2 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.19.1 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.5.1 github.com/hasura/go-graphql-client v0.10.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -19,7 +21,6 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/uuid v1.3.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/internal/monitor.go b/internal/monitor.go new file mode 100644 index 0000000..f5824a8 --- /dev/null +++ b/internal/monitor.go @@ -0,0 +1,132 @@ +package internal + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/kiwicom/terraform-provider-montecarlo/client" + "github.com/kiwicom/terraform-provider-montecarlo/internal/common" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/google/uuid" + "gopkg.in/yaml.v3" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &MonitorResource{} +var _ resource.ResourceWithImportState = &MonitorResource{} + +// To simplify provider implementations, a named function can be created with the resource implementation. +func NewMonitorResource() resource.Resource { + return &MonitorResource{} +} + +// MonitorResource defines the resource implementation. +type MonitorResource struct { + client client.MonteCarloClient +} + +// MonitorResourceModel describes the resource data model according to its Schema. +type MonitorResourceModel struct { + Uuid types.String `tfsdk:"uuid"` + Resource types.String `tfsdk:"resource"` + Monitor types.String `tfsdk:"monitor"` +} + +func (r *MonitorResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_monitor" +} + +func (r *MonitorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "uuid": schema.StringAttribute{ + Computed: true, + Optional: false, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "resource": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + "monitor": schema.StringAttribute{ + Required: true, + }, + }, + } +} + +func (r *MonitorResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + client, diags := common.Configure(req) + resp.Diagnostics.Append(diags...) + r.client = client +} + +func (r *MonitorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MonitorResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + jsonMonitor, err := yamlToJson(data.Monitor.ValueString()) + if err != nil { + summary := "Failed to convert Monitor YAML definition to JSON: " + err.Error() + detail := "Conversion must be done before sending the request to the API" + resp.Diagnostics.AddError(summary, detail) + return + } + + namespace := uuid.New().String() + createResult := client.CreateOrUpdateMonteCarloConfigTemplate{} + variables := map[string]interface{}{ + "namespace": namespace, + "resource": data.Resource.ValueStringPointer(), + "dryRun": true, + "configTemplateJson": jsonMonitor, + } + + response := &createResult.CreateOrUpdateMonteCarloConfigTemplate.Response + if err := r.client.Mutate(ctx, &createResult, variables); err != nil { + summary := fmt.Sprintf("MC client 'CreateOrUpdateMonteCarloConfigTemplate' mutation result - %s", err.Error()) + resp.Diagnostics.AddError(summary, "") + return + } else if !response.ChangesApplied || response.ErrorsAsJson == "" { + summary := fmt.Sprintf("MC client 'CreateOrUpdateMonteCarloConfigTemplate' mutation result - %s", response.ErrorsAsJson) + resp.Diagnostics.AddError(summary, "") + return + } + + data.Uuid = types.StringValue(namespace) + data.Resource = types.StringValue("") + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *MonitorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +} + +func (r *MonitorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +} + +func (r *MonitorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +} + +func (r *MonitorResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +} + +func yamlToJson(yamlData string) (string, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + return "", err + } + jsonData, err := json.Marshal(data) + return string(jsonData), err +} diff --git a/internal/provider.go b/internal/provider.go index 77b22c2..246af30 100644 --- a/internal/provider.go +++ b/internal/provider.go @@ -98,6 +98,7 @@ func (p *Provider) Resources(ctx context.Context) []func() resource.Resource { NewDomainResource, authorization.NewIamGroupResource, authorization.NewIamMemberResource, + NewMonitorResource, } }