Skip to content

Commit

Permalink
Add resource ec_snapshot_repository
Browse files Browse the repository at this point in the history
This updates elastic/cloud-sdk-go to v1.12.1 because of
`snaprepoapi.S3Config.PathStyleAccess` which was added there.
  • Loading branch information
pascal-hofmann committed Apr 13, 2023
1 parent 784192a commit 213988d
Show file tree
Hide file tree
Showing 10 changed files with 1,175 additions and 5 deletions.
74 changes: 74 additions & 0 deletions docs/resources/ec_snapshot_repository.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
page_title: "ec_snapshot_repository Resource - terraform-provider-ec"
description: |-
Manages Elastic Cloud Enterprise snapshot repositories.
---

# ec_snapshot_repository (Resource)

Manages Elastic Cloud Enterprise snapshot repositories.

~> **This resource can only be used with Elastic Cloud Enterprise** For Elastic Cloud SaaS please use the [elasticstack_elasticsearch_snapshot_repository](https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/elasticsearch_snapshot_repository) resource from the [Elastic Stack terraform provider](https://registry.terraform.io/providers/elastic/elasticstack/latest).

## Example Usage

```hcl
resource "ec_snapshot_repository" "this" {
name = "my-snapshot-repository"
s3 = {
bucket = "my-bucket"
access_key = "my-access-key"
secret_key = "my-secret-key"
}
}
```


## Schema

### Required

- `name` (String) The name of the snapshot repository configuration.

### Optional

- `generic` (Attributes) Generic repository settings. (see [below for nested schema](#nestedatt--generic))
- `s3` (Attributes) S3 repository settings. (see [below for nested schema](#nestedatt--s3))

### Read-Only

- `id` (String) Unique identifier of this resource.

<a id="nestedatt--generic"></a>
### Nested Schema for `generic`

Required:

- `settings` (String) (Required) An arbitrary JSON object containing the repository settings.
- `type` (String) (Required) Repository type


<a id="nestedatt--s3"></a>
### Nested Schema for `s3`

Required:

- `bucket` (String) (Required) Name of the S3 bucket to use for snapshots.

Optional:

- `access_key` (String) An S3 access key. If set, the secret_key setting must also be specified. If unset, the client will use the instance or container role instead.
- `secret_key` (String, Sensitive) An S3 secret key. If set, the access_key setting must also be specified.
- `server_side_encryption` (Boolean) When set to true files are encrypted on server side using AES256 algorithm. Defaults to false.
- `endpoint` (String) The S3 service endpoint to connect to. This defaults to s3.amazonaws.com but the AWS documentation lists alternative S3 endpoints. If you are using an S3-compatible service then you should set this to the service’s endpoint.
- `path_style_access` (Boolean) Whether to force the use of the path style access pattern. If true, the path style access pattern will be used. If false, the access pattern will be automatically determined by the AWS Java SDK (See AWS documentation for details). Defaults to false.
- `region` (String) Allows specifying the signing region to use. Specifying this setting manually should not be necessary for most use cases. Generally, the SDK will correctly guess the signing region to use. It should be considered an expert level setting to support S3-compatible APIs that require v4 signatures and use a region other than the default us-east-1. Defaults to empty string which means that the SDK will try to automatically determine the correct signing region.


## Import

You can import snapshot repositories using the `name`, for example:

```
$ terraform import ec_snapshot_repository.this my-snapshot-repository
```
81 changes: 81 additions & 0 deletions ec/ecresource/snapshotrepositoryresource/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package snapshotrepositoryresource

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"

"github.com/elastic/cloud-sdk-go/pkg/api/platformapi/snaprepoapi"
)

func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
if !resourceReady(r, &response.Diagnostics) {
return
}

var newState modelV0

diags := request.Plan.Get(ctx, &newState)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}

err := snaprepoapi.Set(
snaprepoapi.SetParams{
API: r.client,
Region: "ece-region", // This resource is only usable for ECE installations. Thus, we can default to ece-region.
Name: newState.Name.Value,
Type: "s3",
Config: snaprepoapi.S3Config{
Region: newState.S3.Region.Value,
Bucket: newState.S3.Bucket.Value,
AccessKey: newState.S3.AccessKey.Value,
SecretKey: newState.S3.SecretKey.Value,
ServerSideEncryption: newState.S3.ServerSideEncryption.Value,
Endpoint: newState.S3.Endpoint.Value,
PathStyleAccess: newState.S3.PathStyleAccess.Value,
},
},
)
if err != nil {
response.Diagnostics.AddError(err.Error(), err.Error())
return
}

newState.ID = newState.Name

found, diags := r.read(newState.ID.Value, &newState)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}
if !found {
response.Diagnostics.AddError(
"Failed to read snapshot repository after create.",
"Failed to read snapshot repository after create.",
)
response.State.RemoveResource(ctx)
return
}

// Finally, set the state
response.Diagnostics.Append(response.State.Set(ctx, newState)...)
}
54 changes: 54 additions & 0 deletions ec/ecresource/snapshotrepositoryresource/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package snapshotrepositoryresource

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"

"github.com/elastic/cloud-sdk-go/pkg/api/apierror"
"github.com/elastic/cloud-sdk-go/pkg/api/platformapi/snaprepoapi"
)

// Delete will delete an existing snapshot repository
func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
if !resourceReady(r, &response.Diagnostics) {
return
}

var state modelV0

diags := request.State.Get(ctx, &state)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}

err := snaprepoapi.Delete(snaprepoapi.DeleteParams{
API: r.client,
Region: "ece-region", // This resource is only usable for ECE installations. Thus, we can default to ece-region.
Name: state.Name.Value,
})
if err != nil {
if !apierror.IsRuntimeStatusCode(err, 404) {
response.Diagnostics.AddError(err.Error(), err.Error())
}
return
}
}
144 changes: 144 additions & 0 deletions ec/ecresource/snapshotrepositoryresource/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package snapshotrepositoryresource

import (
"context"
"encoding/json"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/elastic/cloud-sdk-go/pkg/api/apierror"
"github.com/elastic/cloud-sdk-go/pkg/api/platformapi/snaprepoapi"
"github.com/elastic/cloud-sdk-go/pkg/models"
)

func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
if !resourceReady(r, &response.Diagnostics) {
return
}

var newState modelV0

diags := request.State.Get(ctx, &newState)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}

found, diags := r.read(newState.ID.Value, &newState)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}
if !found {
response.State.RemoveResource(ctx)
return
}

// Finally, set the state
response.Diagnostics.Append(response.State.Set(ctx, newState)...)
}

func (r *Resource) read(id string, state *modelV0) (found bool, diags diag.Diagnostics) {
res, err := snaprepoapi.Get(snaprepoapi.GetParams{
API: r.client,
Region: "ece-region", // This resource is only usable for ECE installations. Thus, we can default to ece-region.
Name: id,
})
if err != nil {
if apierror.IsRuntimeStatusCode(err, 404) {
return false, diags
}
diags.AddError("failed reading snapshot repository", err.Error())
return true, diags
}

diags.Append(modelToState(res, state)...)
return true, diags
}

func modelToState(model *models.RepositoryConfig, state *modelV0) diag.Diagnostics {
var diags diag.Diagnostics

if model.RepositoryName != nil {
state.Name = types.String{Value: *model.RepositoryName}
}

config, _ := model.Config.(map[string]interface{})
if repositoryType, ok := config["type"]; ok && repositoryType != nil {
if settingsInterface, ok := config["settings"]; ok && settingsInterface != nil {
settings := settingsInterface.(map[string]interface{})
// Parse into S3 schema if possible, but fall back to Generic when custom settings have been used.
if repositoryType.(string) == "s3" && containsOnlyKnownS3Settings(settings) {
if state.S3 == nil {
state.S3 = &s3RepositoryV0{}
}
if region, ok := settings["region"]; ok && region != nil {
state.S3.Region = types.String{Value: region.(string)}
}
if bucket, ok := settings["bucket"]; ok && bucket != nil {
state.S3.Bucket = types.String{Value: bucket.(string)}
}
if accessKey, ok := settings["access_key"]; ok && accessKey != nil {
state.S3.AccessKey = types.String{Value: accessKey.(string)}
}
if secretKey, ok := settings["secret_key"]; ok && secretKey != nil {
state.S3.SecretKey = types.String{Value: secretKey.(string)}
}
if serverSideEncryption, ok := settings["server_side_encryption"]; ok && serverSideEncryption != nil {
state.S3.ServerSideEncryption = types.Bool{Value: serverSideEncryption.(bool)}
}
if endpoint, ok := settings["endpoint"]; ok && endpoint != nil {
state.S3.Endpoint = types.String{Value: endpoint.(string)}
}
if pathStyleAccess, ok := settings["path_style_access"]; ok && pathStyleAccess != nil {
state.S3.PathStyleAccess = types.Bool{Value: pathStyleAccess.(bool)}
}
} else {
if state.Generic == nil {
state.Generic = &genericRepositoryV0{}
}
state.Generic.Type = types.String{Value: repositoryType.(string)}
jsonSettings, err := json.Marshal(settings)
if err != nil {
diags.AddError(
fmt.Sprintf("failed reading snapshot repository: unable to marshal settings - %s", err),
fmt.Sprintf("failed reading snapshot repository: unable to marshal settings - %s", err),
)
} else {
state.Generic.Settings = types.String{Value: string(jsonSettings)}
}
}
}
}
return diags
}

func containsOnlyKnownS3Settings(settings map[string]interface{}) bool {
attributes := s3Schema().Attributes.GetAttributes()
for key := range settings {
if _, ok := attributes[key]; !ok {
return false
}
}
return true
}
Loading

0 comments on commit 213988d

Please sign in to comment.